diff --git a/.gitignore b/.gitignore index d341f1c..74c1d06 100644 --- a/.gitignore +++ b/.gitignore @@ -1,43 +1,26 @@ -## Based on GitHub's Eclipse .gitignore - -classes/ -run/ -.gradle/ -build/ -gradle-app.setting - -## IntelliJ IDEA - -.idea/ -*.iml -*.iws -*.ipr - -## Eclipse - -eclipse/ -*.pydevproject -.project -.metadata -bin/ -tmp/ -*.tmp -*.bak -*.swp -*~.nib -local.properties -.classpath -.settings/ -.loadpath - -# External tool builders -.externalToolBuilders/ - -# Locally stored "Eclipse launch configurations" +# eclipse +bin *.launch +.settings +.metadata +.classpath +.project -# CDT-specific -.cproject +# idea +out +*.ipr +*.iws +*.iml +.idea -# PDT-specific -.buildpath +# gradle +build +.gradle + +# other +eclipse +run +logs/* + +# Files from Forge MDK +forge*changelog.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..b0cbe2b --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,520 @@ +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 diff --git a/README.md b/README.md deleted file mode 100644 index 52d6175..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -This is the version for Minecraft 1.12. - -1.13 can be found here: https://bitbucket.org/Requios/effortless-building-1.13/src/master/ \ No newline at end of file diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..6904b37 --- /dev/null +++ b/README.txt @@ -0,0 +1,53 @@ +------------------------------------------- +Source installation information for modders +------------------------------------------- +This code follows the Minecraft Forge installation methodology. It will apply +some small patches to the vanilla MCP source code, giving you and it access +to some of the data and functions you need to build a successful mod. + +Note also that the patches are built against "unrenamed" MCP source code (aka +srgnames) - this means that you will not be able to read them directly against +normal code. + +Source pack installation information: + +Standalone source installation +============================== + +See the Forge Documentation online for more detailed instructions: +http://mcforge.readthedocs.io/en/latest/gettingstarted/ + +Step 1: Open your command-line and browse to the folder where you extracted the zip file. + +Step 2: You're left with a choice. +If you prefer to use Eclipse: +1. Run the following command: "gradlew genEclipseRuns" (./gradlew genEclipseRuns if you are on Mac/Linux) +2. Open Eclipse, Import > Existing Gradle Project > Select Folder + or run "gradlew eclipse" to generate the project. +(Current Issue) +4. Open Project > Run/Debug Settings > Edit runClient and runServer > Environment +5. Edit MOD_CLASSES to show [modid]%%[Path]; 2 times rather then the generated 4. + +If you prefer to use IntelliJ: +1. Open IDEA, and import project. +2. Select your build.gradle file and have it import. +3. Run the following command: "gradlew genIntellijRuns" (./gradlew genIntellijRuns if you are on Mac/Linux) +4. Refresh the Gradle Project in IDEA if required. + +If at any point you are missing libraries in your IDE, or you've run into problems you can run "gradlew --refresh-dependencies" to refresh the local cache. "gradlew clean" to reset everything {this does not affect your code} and then start the processs again. + +Should it still not work, +Refer to #ForgeGradle on EsperNet for more information about the gradle environment. +or the Forge Project Discord discord.gg/UvedJ9m + +Forge source installation +========================= +MinecraftForge ships with this code and installs it as part of the forge +installation process, no further action is required on your part. + +LexManos' Install Video +======================= +https://www.youtube.com/watch?v=8VEdtQLuLO0&feature=youtu.be + +For more details update more often refer to the Forge Forums: +http://www.minecraftforge.net/forum/index.php/topic,14048.0.html diff --git a/build.gradle b/build.gradle index 11227a0..eb139a2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,82 +1,140 @@ buildscript { repositories { - jcenter() - maven { url = "http://files.minecraftforge.net/maven" } + maven { url = 'https://files.minecraftforge.net/maven' } + mavenCentral() } dependencies { - classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' + classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1+', changing: true } } -apply plugin: 'net.minecraftforge.gradle.forge' -//Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. +apply plugin: 'net.minecraftforge.gradle' +// Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. +apply plugin: 'eclipse' +apply plugin: 'maven-publish' +version = '1.18-2.34' +group = 'nl.requios.effortlessbuilding' // http://maven.apache.org/guides/mini/guide-naming-conventions.html +archivesBaseName = 'effortlessbuilding' -version = "1.12.2-2.13" -group = "nl.requios.effortlessbuilding" // http://maven.apache.org/guides/mini/guide-naming-conventions.html -archivesBaseName = "effortlessbuilding" - -sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. -compileJava { - sourceCompatibility = targetCompatibility = '1.8' -} +// Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17. +java.toolchain.languageVersion = JavaLanguageVersion.of(17) minecraft { - version = "1.12.2-14.23.5.2825" - runDir = "run" - - // 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. + // 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 = "stable_39" + // Simply re-run your setup task after changing the mappings to update your workspace. + mappings channel: 'official', version: '1.18.2' + accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. -} -repositories { - flatDir { dirs 'libs' } + // Default run configurations. + // These can be tweaked, removed, or duplicated as needed. + runs { + client { + workingDirectory project.file('run') + + // Recommended logging data for a userdev environment (SCAN,REGISTRIES,REGISTRYDUMP) + property 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + property 'forge.logging.console.level', 'debug' + + mods { + effortlessbuilding { + source sourceSets.main + } + } + } + + server { + workingDirectory project.file('run') + + // Recommended logging data for a userdev environment (SCAN,REGISTRIES,REGISTRYDUMP) + property 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + property 'forge.logging.console.level', 'debug' + + mods { + effortlessbuilding { + source sourceSets.main + } + } + } + + data { + workingDirectory project.file('run') + + // Recommended logging data for a userdev environment (SCAN,REGISTRIES,REGISTRYDUMP) + property 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + property 'forge.logging.console.level', 'debug' + + // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. + args '--mod', 'effortlessbuilding', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') + + mods { + effortlessbuilding { + source sourceSets.main + } + } + } + } } dependencies { - // 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" - - // 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 + // 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.18.2-40.0.19' - // 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' + // Real mod deobf dependency examples - these get remapped to your current mappings + // compileOnly fg.deobf("mezz.jei:jei-${mc_version}:${jei_version}:api") // Adds JEI API as a compile dependency + // runtimeOnly fg.deobf("mezz.jei:jei-${mc_version}:${jei_version}") // Adds the full JEI mod as a runtime dependency + // implementation fg.deobf("com.tterrag.registrate:Registrate:MC${mc_version}-${registrate_version}") // Adds registrate as a dependency - // the deobf configurations: 'deobfCompile' and 'deobfProvided' are the same as the normal compile and provided, - // except that these dependencies get remapped to your current MCP mappings - //deobfCompile 'com.mod-buildcraft:buildcraft:6.0.8:dev' - //deobfProvided 'com.mod-buildcraft:buildcraft:6.0.8:dev' - provided 'mod.chiselsandbits:chiselsandbits:14.30' + // Examples using mod jars from ./libs + // implementation fg.deobf("blank:coolmod-${mc_version}:${coolmod_version}") - // for more info... + // For more info... // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html // http://www.gradle.org/docs/current/userguide/dependency_management.html } -processResources { - // this will ensure that this task is redone when the versions change. - inputs.property "version", project.version - inputs.property "mcversion", project.minecraft.version - - // replace stuff in mcmod.info, nothing else - from(sourceSets.main.resources.srcDirs) { - include 'mcmod.info' - - // replace version and mcversion - expand 'version':project.version, 'mcversion':project.minecraft.version - } - - // copy everything else except the mcmod.info - from(sourceSets.main.resources.srcDirs) { - exclude 'mcmod.info' +// Example for how to get properties into the manifest for reading by the runtime.. +jar { + manifest { + attributes([ + "Specification-Title": "effortlessbuilding", + "Specification-Vendor": "requios", + "Specification-Version": "1", // We are version 1 of ourselves + "Implementation-Title": project.name, + "Implementation-Version": "${version}", + "Implementation-Vendor" :"requios", + "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") + ]) } } + +// Example configuration to allow publishing using the maven-publish task +// This is the preferred method to reobfuscate your jar file +jar.finalizedBy('reobfJar') +// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing +//publish.dependsOn('reobfJar') + +publishing { + publications { + mavenJava(MavenPublication) { + artifact jar + } + } + repositories { + maven { + url "file:///${project.projectDir}/mcmodsrepo" + } + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..878bf1f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,4 @@ +# Sets default memory used for gradle commands. Can be overridden by user or command line properties. +# This is required to provide enough memory for the Minecraft decompilation process. +org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 758de96..7454180 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2d80b69..e750102 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d..c53aefa 100644 --- a/gradlew +++ b/gradlew @@ -1,78 +1,129 @@ -#!/usr/bin/env sh +#!/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. +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# 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/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +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 done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # 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 - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | 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 @@ -81,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + 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 @@ -89,84 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") +# Collect all arguments for the java command, 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. -# 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" +# 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" ) -# 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")" + 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" ) + 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 + done fi +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# 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" "$@" diff --git a/gradlew.bat b/gradlew.bat index f955316..107acd3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/libs/chiselsandbits-14.30.jar b/libs/chiselsandbits-14.30.jar deleted file mode 100644 index 3598b64..0000000 Binary files a/libs/chiselsandbits-14.30.jar and /dev/null differ diff --git a/src/main/java/nl/requios/effortlessbuilding/BuildConfig.java b/src/main/java/nl/requios/effortlessbuilding/BuildConfig.java index 1663000..2c84c58 100644 --- a/src/main/java/nl/requios/effortlessbuilding/BuildConfig.java +++ b/src/main/java/nl/requios/effortlessbuilding/BuildConfig.java @@ -1,89 +1,114 @@ package nl.requios.effortlessbuilding; -import net.minecraftforge.common.config.Config; +import net.minecraftforge.common.ForgeConfigSpec; -import static net.minecraftforge.common.config.Config.*; - -@Config(modid = EffortlessBuilding.MODID, name = "EffortlessBuilding", type = Type.INSTANCE, category = "") public class BuildConfig { - public static Reach reach = new Reach(); - public static SurvivalBalancers survivalBalancers = new SurvivalBalancers(); - public static Visuals visuals = new Visuals(); + 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 { - @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."}) - public boolean enableReachUpgrades = true; + public static class Reach { + public final ForgeConfigSpec.ConfigValue enableReachUpgrades; + public final ForgeConfigSpec.ConfigValue maxReachCreative; + public final ForgeConfigSpec.ConfigValue maxReachLevel0; + public final ForgeConfigSpec.ConfigValue maxReachLevel1; + public final ForgeConfigSpec.ConfigValue maxReachLevel2; + public final ForgeConfigSpec.ConfigValue maxReachLevel3; - @Comment({"Maximum reach in creative", - "Keep in mind that chunks need to be loaded to be able to place blocks inside."}) - public int maxReachCreative = 200; + 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); - @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."}) - public int maxReachLevel0 = 20; + 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); - @Comment("Maximum reach in survival with one upgrade") - public int maxReachLevel1 = 50; + 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); - @Comment("Maximum reach in survival with two upgrades") - public int maxReachLevel2 = 100; + maxReachLevel1 = builder + .comment("Maximum reach in survival with one upgrade") + .define("maxReachLevel1", 50); - @Comment("Maximum reach in survival with three upgrades") - public int maxReachLevel3 = 200; - } + maxReachLevel2 = builder + .comment("Maximum reach in survival with two upgrades") + .define("maxReachLevel2", 100); - public static class SurvivalBalancers { - @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."}) - public boolean breakFar = false; + maxReachLevel3 = builder + .comment("Maximum reach in survival with three upgrades") + .define("maxReachLevel3", 200); - @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."}) - public boolean increasedMiningTime = true; + builder.pop(); + } + } - @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%."}) - @RangeInt(min = 0, max = 200) - public int miningTimePercentage = 50; + public static class SurvivalBalancers { + public final ForgeConfigSpec.ConfigValue quickReplaceMiningLevel; + public final ForgeConfigSpec.ConfigValue undoStackSize; - @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", - }) - @RangeInt(min = -1, max = 3) - public int quickReplaceMiningLevel = -1; + public SurvivalBalancers(ForgeConfigSpec.Builder builder) { + builder.push("SurvivalBalancers"); - @Comment({"How many placements are remembered for the undo functionality."}) - @RequiresMcRestart - public int undoStackSize = 10; - } + quickReplaceMiningLevel = builder + .comment("Determines what blocks can be replaced in survival.", + "-1: only blocks that can be harvested by hand (default)", + "0: blocks that can be harvested with wooden or gold tools", + "1: blocks that can be harvested with stone tools", + "2: blocks that can be harvested with iron tools", + "3: blocks that can be harvested with diamond tools", + "4: blocks that can be harvested with netherite tools") + .defineInRange("quickReplaceMiningLevel", -1, -1, 3); - public static class Visuals { - @Comment({"Show a block preview if you have a block in hand on build mode NORMAL"}) - public boolean alwaysShowBlockPreview = false; + undoStackSize = builder + .comment("How many placements are remembered for the undo functionality.") + .worldRestart() + .define("undoStackSize", 10); - @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"}) - public double dissolveTimeMultiplier = 1.0; + builder.pop(); + } + } - @Comment({"Switch to using the simple performance shader when placing more than this many blocks."}) - public int shaderTreshold = 1500; + public static class Visuals { + public final ForgeConfigSpec.ConfigValue alwaysShowBlockPreview; + public final ForgeConfigSpec.ConfigValue dissolveTimeMultiplier; + public final ForgeConfigSpec.ConfigValue shaderThreshold; + public final ForgeConfigSpec.ConfigValue useShaders; - @Comment({"Use fancy shaders while placing blocks"}) - public boolean useShaders = true; + public Visuals(ForgeConfigSpec.Builder builder) { + builder.push("Visuals"); - } + alwaysShowBlockPreview = builder + .comment("Show a block preview if you have a block in hand even in the 'Normal' build mode") + .define("alwaysShowBlockPreview", false); + + dissolveTimeMultiplier = builder + .comment("How long the dissolve effect takes when placing blocks.", + "Default between 30 and 60 ticks, you can multiply that here.", + "Recommended values:", + "Snappy: 0.7", + "Relaxing: 1.5") + .define("dissolveTimeMultiplier", 1.0); + + shaderThreshold = builder + .comment("Switch to using the simple performance shader when placing more than this many blocks.") + .define("shaderTreshold", 1500); + + useShaders = builder + .comment("Use fancy shaders while placing blocks") + .define("useShaders", true); + + builder.pop(); + } + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java index b356e97..4dea3e7 100644 --- a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java +++ b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java @@ -1,150 +1,113 @@ package nl.requios.effortlessbuilding; -import net.minecraft.block.Block; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.Item; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.SoundEvent; -import net.minecraft.util.text.TextComponentString; -import net.minecraftforge.common.capabilities.CapabilityManager; -import net.minecraftforge.common.config.Config; -import net.minecraftforge.common.config.ConfigManager; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.Item; +import net.minecraft.network.chat.TextComponent; +import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.Mod.EventHandler; -import net.minecraftforge.fml.common.SidedProxy; -import net.minecraftforge.fml.common.event.FMLInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -import net.minecraftforge.fml.common.event.FMLServerStartingEvent; -import net.minecraftforge.fml.common.network.NetworkRegistry; -import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper; -import net.minecraftforge.fml.relauncher.Side; -import nl.requios.effortlessbuilding.buildmode.BuildModes; +import net.minecraftforge.fml.config.ModConfig; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.network.IContainerFactory; +import net.minecraftforge.registries.RegistryObject; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; import nl.requios.effortlessbuilding.capability.ModeCapabilityManager; import nl.requios.effortlessbuilding.capability.ModifierCapabilityManager; -import nl.requios.effortlessbuilding.command.CommandReach; import nl.requios.effortlessbuilding.compatibility.CompatHelper; -import nl.requios.effortlessbuilding.gui.RandomizerBagGuiHandler; -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.*; +import nl.requios.effortlessbuilding.gui.DiamondRandomizerBagContainer; +import nl.requios.effortlessbuilding.gui.GoldenRandomizerBagContainer; +import nl.requios.effortlessbuilding.gui.RandomizerBagContainer; +import nl.requios.effortlessbuilding.item.*; +import nl.requios.effortlessbuilding.network.PacketHandler; +import nl.requios.effortlessbuilding.proxy.ClientProxy; import nl.requios.effortlessbuilding.proxy.IProxy; +import nl.requios.effortlessbuilding.proxy.ServerProxy; +import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -@Mod(modid = EffortlessBuilding.MODID, name = EffortlessBuilding.NAME, version = EffortlessBuilding.VERSION) -@Mod.EventBusSubscriber -public class EffortlessBuilding -{ - public static final String MODID = "effortlessbuilding"; - public static final String NAME = "Effortless Building"; - public static final String VERSION = "1.12.2-2.13"; +@Mod(EffortlessBuilding.MODID) +public class EffortlessBuilding { - @Mod.Instance(EffortlessBuilding.MODID) - public static EffortlessBuilding instance; + public static final String MODID = "effortlessbuilding"; + public static final Logger logger = LogManager.getLogger(); - public static Logger logger; + public static EffortlessBuilding instance; + public static IProxy proxy = DistExecutor.runForDist(() -> ClientProxy::new, () -> ServerProxy::new); - @SidedProxy( - clientSide="nl.requios.effortlessbuilding.proxy.ClientProxy", - serverSide="nl.requios.effortlessbuilding.proxy.ServerProxy" - ) - public static IProxy proxy; + //Registration + private static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID); + private static final DeferredRegister> CONTAINERS = DeferredRegister.create(ForgeRegistries.CONTAINERS, EffortlessBuilding.MODID); - public static final SimpleNetworkWrapper packetHandler = NetworkRegistry.INSTANCE.newSimpleChannel(EffortlessBuilding.MODID); + public static final RegistryObject RANDOMIZER_BAG_ITEM = ITEMS.register("randomizer_bag", RandomizerBagItem::new); + public static final RegistryObject GOLDEN_RANDOMIZER_BAG_ITEM = ITEMS.register("golden_randomizer_bag", GoldenRandomizerBagItem::new); + public static final RegistryObject DIAMOND_RANDOMIZER_BAG_ITEM = ITEMS.register("diamond_randomizer_bag", DiamondRandomizerBagItem::new); + public static final RegistryObject REACH_UPGRADE_1_ITEM = ITEMS.register("reach_upgrade1", ReachUpgrade1Item::new); + public static final RegistryObject REACH_UPGRADE_2_ITEM = ITEMS.register("reach_upgrade2", ReachUpgrade2Item::new); + public static final RegistryObject REACH_UPGRADE_3_ITEM = ITEMS.register("reach_upgrade3", ReachUpgrade3Item::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 RegistryObject> RANDOMIZER_BAG_CONTAINER = CONTAINERS.register("randomizer_bag", () -> registerContainer(RandomizerBagContainer::new)); + public static final RegistryObject> GOLDEN_RANDOMIZER_BAG_CONTAINER = CONTAINERS.register("golden_randomizer_bag", () -> registerContainer(GoldenRandomizerBagContainer::new)); + public static final RegistryObject> DIAMOND_RANDOMIZER_BAG_CONTAINER = CONTAINERS.register("diamond_randomizer_bag", () -> registerContainer(DiamondRandomizerBagContainer::new)); - public static final Block[] BLOCKS = { - }; + public EffortlessBuilding() { + instance = this; - public static final Item[] ITEMS = { - ITEM_RANDOMIZER_BAG, - ITEM_REACH_UPGRADE_1, - ITEM_REACH_UPGRADE_2, - ITEM_REACH_UPGRADE_3 - }; + // Register ourselves for server and other game events we are interested in + FMLJavaModLoadingContext.get().getModEventBus().register(this); - public static final int RANDOMIZER_BAG_GUI = 0; + ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus()); + CONTAINERS.register(FMLJavaModLoadingContext.get().getModEventBus()); - @EventHandler - // preInit "Run before anything else. Read your config, create blocks, items, etc, and register them with the GameRegistry." - public void preInit(FMLPreInitializationEvent event) - { - logger = event.getModLog(); + //Register config + ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, BuildConfig.spec); + } - CapabilityManager.INSTANCE.register(ModifierCapabilityManager.IModifierCapability.class, new ModifierCapabilityManager.Storage(), ModifierCapabilityManager.ModifierCapability.class); - CapabilityManager.INSTANCE.register(ModeCapabilityManager.IModeCapability.class, new ModeCapabilityManager.Storage(), ModeCapabilityManager.ModeCapability.class); + public static MenuType registerContainer(IContainerFactory fact){ + MenuType type = new MenuType(fact); + return type; + } - EffortlessBuilding.packetHandler.registerMessage(ModifierSettingsMessage.MessageHandler.class, ModifierSettingsMessage.class, 0, Side.SERVER); - EffortlessBuilding.packetHandler.registerMessage(ModifierSettingsMessage.MessageHandler.class, ModifierSettingsMessage.class, 0, Side.CLIENT); + @SubscribeEvent + public void setup(final FMLCommonSetupEvent event) { + PacketHandler.register(); - EffortlessBuilding.packetHandler.registerMessage(ModeSettingsMessage.MessageHandler.class, ModeSettingsMessage.class, 1, Side.SERVER); - EffortlessBuilding.packetHandler.registerMessage(ModeSettingsMessage.MessageHandler.class, ModeSettingsMessage.class, 1, Side.CLIENT); + proxy.setup(event); - EffortlessBuilding.packetHandler.registerMessage(ModeActionMessage.MessageHandler.class, ModeActionMessage.class, 2, Side.SERVER); - EffortlessBuilding.packetHandler.registerMessage(ModeActionMessage.MessageHandler.class, ModeActionMessage.class, 2, Side.CLIENT); + CompatHelper.setup(); + } - EffortlessBuilding.packetHandler.registerMessage(BlockPlacedMessage.MessageHandler.class, BlockPlacedMessage.class, 3, Side.SERVER); - EffortlessBuilding.packetHandler.registerMessage(BlockPlacedMessage.MessageHandler.class, BlockPlacedMessage.class, 3, Side.CLIENT); + @SubscribeEvent + public void clientSetup(final FMLClientSetupEvent event) { + proxy.clientSetup(event); + } - EffortlessBuilding.packetHandler.registerMessage(BlockBrokenMessage.MessageHandler.class, BlockBrokenMessage.class, 4, Side.SERVER); - EffortlessBuilding.packetHandler.registerMessage(BlockBrokenMessage.MessageHandler.class, BlockBrokenMessage.class, 4, Side.CLIENT); + @SubscribeEvent + public void registerCapabilities(RegisterCapabilitiesEvent event){ + event.register(ModifierCapabilityManager.IModifierCapability.class); + event.register(ModeCapabilityManager.IModeCapability.class); + } - EffortlessBuilding.packetHandler.registerMessage(CancelModeMessage.MessageHandler.class, CancelModeMessage.class, 5, Side.SERVER); - EffortlessBuilding.packetHandler.registerMessage(CancelModeMessage.MessageHandler.class, CancelModeMessage.class, 5, Side.CLIENT); + public static void log(String msg) { + logger.info(msg); + } - EffortlessBuilding.packetHandler.registerMessage(RequestLookAtMessage.MessageHandler.class, RequestLookAtMessage.class, 6, Side.SERVER); - EffortlessBuilding.packetHandler.registerMessage(RequestLookAtMessage.MessageHandler.class, RequestLookAtMessage.class, 6, Side.CLIENT); + public static void log(Player player, String msg) { + log(player, msg, false); + } - EffortlessBuilding.packetHandler.registerMessage(AddUndoMessage.MessageHandler.class, AddUndoMessage.class, 7, Side.SERVER); - EffortlessBuilding.packetHandler.registerMessage(AddUndoMessage.MessageHandler.class, AddUndoMessage.class, 7, Side.CLIENT); + public static void log(Player player, String msg, boolean actionBar) { + player.displayClientMessage(new TextComponent(msg), actionBar); + } - EffortlessBuilding.packetHandler.registerMessage(ClearUndoMessage.MessageHandler.class, ClearUndoMessage.class, 8, Side.SERVER); - EffortlessBuilding.packetHandler.registerMessage(ClearUndoMessage.MessageHandler.class, ClearUndoMessage.class, 8, Side.CLIENT); - - proxy.preInit(event); - } - - @EventHandler - // Do your mod setup. Build whatever data structures you care about. - // Register network handlers - public void init(FMLInitializationEvent event) - { - ConfigManager.sync(MODID, Config.Type.INSTANCE); - NetworkRegistry.INSTANCE.registerGuiHandler(EffortlessBuilding.instance, new RandomizerBagGuiHandler()); - - proxy.init(event); - } - - @EventHandler - // postInit "Handle interaction with other mods, complete your setup based on this." - public void postInit(FMLPostInitializationEvent event) - { - proxy.postInit(event); - CompatHelper.postInit(); - } - - @EventHandler - public void serverStarting(FMLServerStartingEvent event) - { - event.registerServerCommand(new CommandReach()); - proxy.serverStarting(event); - } - - public static void log(String msg){ - logger.info(msg); - } - - public static void log(EntityPlayer player, String msg){ - log(player, msg, false); - } - - public static void log(EntityPlayer player, String msg, boolean actionBar){ - player.sendStatusMessage(new TextComponentString(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) { + proxy.logTranslate(player, prefix, translationKey, suffix, actionBar); + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/EventHandler.java b/src/main/java/nl/requios/effortlessbuilding/EventHandler.java index e56b66f..84c65ca 100644 --- a/src/main/java/nl/requios/effortlessbuilding/EventHandler.java +++ b/src/main/java/nl/requios/effortlessbuilding/EventHandler.java @@ -1,206 +1,163 @@ package nl.requios.effortlessbuilding; -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.init.Blocks; -import net.minecraft.item.Item; -import net.minecraft.item.ItemBlock; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.World; -import net.minecraftforge.common.config.Config; -import net.minecraftforge.common.config.ConfigManager; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.event.AttachCapabilitiesEvent; -import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.world.BlockEvent; -import net.minecraftforge.fml.client.event.ConfigChangedEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.network.PacketDistributor; import nl.requios.effortlessbuilding.buildmode.BuildModes; import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; -import nl.requios.effortlessbuilding.buildmodifier.BlockSet; -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.BlockBrokenMessage; import nl.requios.effortlessbuilding.network.ClearUndoMessage; +import nl.requios.effortlessbuilding.network.PacketHandler; import nl.requios.effortlessbuilding.network.RequestLookAtMessage; -import scala.actors.threadpool.Arrays; -import java.util.ArrayList; -import java.util.List; +@Mod.EventBusSubscriber(modid = EffortlessBuilding.MODID, bus = Mod.EventBusSubscriber.Bus.FORGE) +public class EventHandler { -import static net.minecraftforge.fml.common.gameevent.PlayerEvent.*; + @SubscribeEvent + public static void attachCapabilities(AttachCapabilitiesEvent event) { + if (event.getObject() instanceof FakePlayer) return; + if (event.getObject() instanceof Player) { + event.addCapability(new ResourceLocation(EffortlessBuilding.MODID, "build_modifier"), new ModifierCapabilityManager.Provider()); + event.addCapability(new ResourceLocation(EffortlessBuilding.MODID, "build_mode"), new ModeCapabilityManager.Provider()); + } + } -@Mod.EventBusSubscriber -public class EventHandler -{ + @SubscribeEvent + public static void onBlockPlaced(BlockEvent.EntityPlaceEvent event) { + if (event.getWorld().isClientSide()) return; - @SubscribeEvent - public static void registerBlocks(RegistryEvent.Register event) - { - event.getRegistry().registerAll(EffortlessBuilding.BLOCKS); - } + if (!(event.getEntity() instanceof Player)) return; - @SubscribeEvent - public static void registerItems(RegistryEvent.Register event) - { - event.getRegistry().registerAll(EffortlessBuilding.ITEMS); + if (event.getEntity() instanceof FakePlayer) return; - for (Block block : EffortlessBuilding.BLOCKS) - { - event.getRegistry().register(new ItemBlock(block).setRegistryName(block.getRegistryName())); - } - } + //Cancel event if necessary + ServerPlayer player = ((ServerPlayer) event.getEntity()); + BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode(); + ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - @SubscribeEvent - public static void attachCapabilities(AttachCapabilitiesEvent event) { - if (event.getObject() instanceof EntityPlayer) { - event.addCapability(new ResourceLocation(EffortlessBuilding.MODID, "BuildModifier"), new ModifierCapabilityManager.Provider()); - event.addCapability(new ResourceLocation(EffortlessBuilding.MODID, "BuildMode"), new ModeCapabilityManager.Provider()); - } - } + 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 - @SubscribeEvent - public static void onConfigChangedEvent(ConfigChangedEvent.OnConfigChangedEvent event) - { - if (event.getModID().equals(EffortlessBuilding.MODID)) - { - ConfigManager.sync(EffortlessBuilding.MODID, Config.Type.INSTANCE); - } - } + //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())); + } -// @SubscribeEvent -// public static void onServerTick(TickEvent.ServerTickEvent event) { +// Stat blocksPlacedStat = StatList.CUSTOM.get(new ResourceLocation(EffortlessBuilding.MODID, "blocks_placed")); +// player.getStats().increment(player, blocksPlacedStat, 1); // -// } +// System.out.println(player.getStats().getValue(blocksPlacedStat)); + } - @SubscribeEvent - //Only called serverside (except with lilypads...) - public static void onBlockPlaced(BlockEvent.PlaceEvent event) { - if (event.getWorld().isRemote) return; + @SubscribeEvent + public static void onBlockBroken(BlockEvent.BreakEvent event) { + if (event.getWorld().isClientSide()) return; - //Cancel event if necessary - EntityPlayer player = event.getPlayer(); - BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode(); - ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); + if (event.getPlayer() instanceof FakePlayer) return; - if (buildMode != BuildModes.BuildModeEnum.NORMAL) { - event.setCanceled(true); - } else if (modifierSettings.doQuickReplace()) { - //Cancel event and send message if QuickReplace - event.setCanceled(true); - EffortlessBuilding.packetHandler.sendTo(new RequestLookAtMessage(true), (EntityPlayerMP) player); - EffortlessBuilding.packetHandler.sendTo(new AddUndoMessage(event.getPos(), event.getBlockSnapshot().getReplacedBlock(), event.getState()), (EntityPlayerMP) player); - } else { - //NORMAL mode, let vanilla handle block placing - //But modifiers should still work + //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); - //Send message to client, which sends message back with raytrace info - EffortlessBuilding.packetHandler.sendTo(new RequestLookAtMessage(false), (EntityPlayerMP) player); - EffortlessBuilding.packetHandler.sendTo(new AddUndoMessage(event.getPos(), event.getBlockSnapshot().getReplacedBlock(), event.getState()), (EntityPlayerMP) player); - } - } + //Add to undo stack in client + if (event.getPlayer() instanceof ServerPlayer && event.getState() != null && event.getPos() != null) { + PacketDistributor.PacketTarget packetTarget = PacketDistributor.PLAYER.with(() -> (ServerPlayer) event.getPlayer()); + if (packetTarget != null) + PacketHandler.INSTANCE.send(packetTarget, new AddUndoMessage(event.getPos(), event.getState(), Blocks.AIR.defaultBlockState())); + } + } + } - @SubscribeEvent - public static void onBlockBroken(BlockEvent.BreakEvent event) { - if (event.getWorld().isRemote) return; + @SubscribeEvent + public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) { + if (event.getPlayer() instanceof FakePlayer) return; + Player player = event.getPlayer(); + ModifierSettingsManager.handleNewPlayer(player); + ModeSettingsManager.handleNewPlayer(player); + } - //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 should still work - //Dont break the original block yourself, otherwise Tinkers Hammer and Veinminer won't work - BuildModes.onBlockBroken(event.getPlayer(), event.getPos(), false); + @SubscribeEvent + public static void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) { + if (event.getPlayer() instanceof FakePlayer) return; + Player player = event.getPlayer(); + if (player.getCommandSenderWorld().isClientSide) return; - //Add to undo stack in client - EffortlessBuilding.packetHandler.sendTo(new AddUndoMessage(event.getPos(), event.getState(), Blocks.AIR.getDefaultState()), (EntityPlayerMP) event.getPlayer()); - } - } + UndoRedo.clear(player); + PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new ClearUndoMessage()); + } - @SubscribeEvent - public static void breakSpeed(PlayerEvent.BreakSpeed event) { - //Disable if config says so - if (!BuildConfig.survivalBalancers.increasedMiningTime) return; + @SubscribeEvent + public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) { + if (event.getPlayer() instanceof FakePlayer) return; + Player player = event.getPlayer(); + ModifierSettingsManager.handleNewPlayer(player); + ModeSettingsManager.handleNewPlayer(player); + } - EntityPlayer player = event.getEntityPlayer(); - World world = player.world; - BlockPos pos = event.getPos(); + @SubscribeEvent + public static void onPlayerChangedDimension(PlayerEvent.PlayerChangedDimensionEvent event) { + if (event.getPlayer() instanceof FakePlayer) return; + Player player = event.getPlayer(); + if (player.getCommandSenderWorld().isClientSide) return; - //EffortlessBuilding.log(player, String.valueOf(event.getNewSpeed())); + //Set build mode to normal + ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); + modeSettings.setBuildMode(BuildModes.BuildModeEnum.NORMAL); + ModeSettingsManager.setModeSettings(player, modeSettings); - float originalBlockHardness = event.getState().getBlockHardness(world, pos); - if (originalBlockHardness < 0) return; //Dont break bedrock - float totalBlockHardness = 0; - //get coordinates - List coordinates = BuildModifiers.findCoordinates(player, pos); - for (int i = 1; i < coordinates.size(); i++) { - BlockPos coordinate = coordinates.get(i); - //get existing blockstates at those coordinates - IBlockState blockState = world.getBlockState(coordinate); - //add hardness for each blockstate, if can break - if (SurvivalHelper.canBreak(world, player, coordinate)) - totalBlockHardness += blockState.getBlockHardness(world, coordinate); - } + //Disable modifiers + ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); + modifierSettings.getMirrorSettings().enabled = false; + modifierSettings.getRadialMirrorSettings().enabled = false; + modifierSettings.getArraySettings().enabled = false; + ModifierSettingsManager.setModifierSettings(player, modifierSettings); - //Grabbing percentage from config - float percentage = (float) BuildConfig.survivalBalancers.miningTimePercentage / 100; - totalBlockHardness *= percentage; - totalBlockHardness += originalBlockHardness; + ModifierSettingsManager.handleNewPlayer(player); + ModeSettingsManager.handleNewPlayer(player); - float newSpeed = event.getOriginalSpeed() / totalBlockHardness * originalBlockHardness; - if (Float.isNaN(newSpeed) || newSpeed == 0f) newSpeed = 1f; - event.setNewSpeed(newSpeed); + UndoRedo.clear(player); + PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new ClearUndoMessage()); + } - //EffortlessBuilding.log(player, String.valueOf(event.getNewSpeed())); - } + @SubscribeEvent + public static void onPlayerClone(PlayerEvent.Clone event) { + if (event.getPlayer() instanceof FakePlayer) return; + //Attach capabilities on death, otherwise crash + Player oldPlayer = event.getOriginal(); + oldPlayer.revive(); - @SubscribeEvent - public static void onPlayerLoggedIn(PlayerLoggedInEvent event) { - EntityPlayer player = event.player; - ModifierSettingsManager.handleNewPlayer(player); - ModeSettingsManager.handleNewPlayer(player); - } - - @SubscribeEvent - public static void onPlayerLoggedOut(PlayerLoggedOutEvent event) { - EntityPlayer player = event.player; - if (player.getEntityWorld().isRemote) return; - - UndoRedo.clear(player); - EffortlessBuilding.packetHandler.sendTo(new ClearUndoMessage(), (EntityPlayerMP) player); - } - - @SubscribeEvent - public static void onPlayerRespawn(PlayerRespawnEvent event) { - EntityPlayer player = event.player; - ModifierSettingsManager.handleNewPlayer(player); - ModeSettingsManager.handleNewPlayer(player); - } - - @SubscribeEvent - public static void onPlayerChangedDimension(PlayerChangedDimensionEvent event) { - EntityPlayer player = event.player; - if (player.getEntityWorld().isRemote) return; - - ModifierSettingsManager.handleNewPlayer(player); - ModeSettingsManager.handleNewPlayer(player); - - UndoRedo.clear(player); - EffortlessBuilding.packetHandler.sendTo(new ClearUndoMessage(), (EntityPlayerMP) player); - } + Player newPlayer = event.getPlayer(); + ModifierSettingsManager.setModifierSettings(newPlayer, ModifierSettingsManager.getModifierSettings(oldPlayer)); + ModeSettingsManager.setModeSettings(newPlayer, ModeSettingsManager.getModeSettings(oldPlayer)); + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/ModClientEventHandler.java b/src/main/java/nl/requios/effortlessbuilding/ModClientEventHandler.java new file mode 100644 index 0000000..b2a3fb3 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/ModClientEventHandler.java @@ -0,0 +1,31 @@ +package nl.requios.effortlessbuilding; + +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.RegisterShadersEvent; +import net.minecraftforge.client.event.TextureStitchEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import nl.requios.effortlessbuilding.buildmode.BuildModes; +import nl.requios.effortlessbuilding.buildmode.ModeOptions; +import nl.requios.effortlessbuilding.render.BuildRenderTypes; + +import java.io.IOException; +import java.util.HashMap; + +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD, value = {Dist.CLIENT}) +public class ModClientEventHandler { + + @SubscribeEvent + public static void registerShaders(RegisterShadersEvent event) throws IOException { + event.registerShader(new ShaderInstance(event.getResourceManager(), + new ResourceLocation(EffortlessBuilding.MODID, "dissolve"), + DefaultVertexFormat.BLOCK), + shaderInstance -> BuildRenderTypes.dissolveShaderInstance = shaderInstance); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/BaseBuildMode.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/BaseBuildMode.java new file mode 100644 index 0000000..4835b72 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/BaseBuildMode.java @@ -0,0 +1,39 @@ +package nl.requios.effortlessbuilding.buildmode; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.Direction; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; + +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.UUID; + +public abstract class BaseBuildMode implements IBuildMode { + //In singleplayer client and server variables are shared + //Split everything that needs separate values and may not be called twice in one click + protected Dictionary rightClickClientTable = new Hashtable<>(); + protected Dictionary rightClickServerTable = new Hashtable<>(); + protected Dictionary firstPosTable = new Hashtable<>(); + protected Dictionary sideHitTable = new Hashtable<>(); + protected Dictionary hitVecTable = new Hashtable<>(); + + @Override + public void initialize(Player player) { + rightClickClientTable.put(player.getUUID(), 0); + rightClickServerTable.put(player.getUUID(), 0); + firstPosTable.put(player.getUUID(), BlockPos.ZERO); + sideHitTable.put(player.getUUID(), Direction.UP); + hitVecTable.put(player.getUUID(), Vec3.ZERO); + } + + @Override + public Direction getSideHit(Player player) { + return sideHitTable.get(player.getUUID()); + } + + @Override + public Vec3 getHitVec(Player player) { + return hitVecTable.get(player.getUUID()); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java index 2c557de..f77aa90 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java @@ -1,14 +1,16 @@ package nl.requios.effortlessbuilding.buildmode; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; +import com.mojang.math.Vector4f; +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.Direction; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; import nl.requios.effortlessbuilding.EffortlessBuilding; -import nl.requios.effortlessbuilding.buildmode.ModeOptions.ActionEnum; -import nl.requios.effortlessbuilding.buildmodifier.*; -import nl.requios.effortlessbuilding.compatibility.CompatHelper; +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; @@ -19,205 +21,280 @@ import java.util.Dictionary; import java.util.Hashtable; import java.util.List; +import static nl.requios.effortlessbuilding.buildmode.ModeOptions.OptionEnum; + public class BuildModes { - //Static variables are shared between client and server in singleplayer - //We need them separate - public static Dictionary currentlyBreakingClient = new Hashtable<>(); - public static Dictionary currentlyBreakingServer = new Hashtable<>(); + //Static variables are shared between client and server in singleplayer + //We need them separate + public static Dictionary currentlyBreakingClient = new Hashtable<>(); + public static Dictionary currentlyBreakingServer = new Hashtable<>(); - public enum BuildModeEnum { - NORMAL("effortlessbuilding.mode.normal", new Normal(), new ActionEnum[]{}), - NORMAL_PLUS("effortlessbuilding.mode.normal_plus", new NormalPlus(), new ActionEnum[]{ActionEnum.NORMAL_SPEED, ActionEnum.FAST_SPEED}), - LINE("effortlessbuilding.mode.line", new Line(), new ActionEnum[]{/*ActionEnum.THICKNESS_1, ActionEnum.THICKNESS_3, ActionEnum.THICKNESS_5*/}), - WALL("effortlessbuilding.mode.wall", new Wall(), new ActionEnum[]{ActionEnum.FULL, ActionEnum.HOLLOW}), - FLOOR("effortlessbuilding.mode.floor", new Floor(), new ActionEnum[]{ActionEnum.FULL, ActionEnum.HOLLOW}), - DIAGONAL_LINE("effortlessbuilding.mode.diagonal_line", new DiagonalLine(), new ActionEnum[]{/*ActionEnum.THICKNESS_1, ActionEnum.THICKNESS_3, ActionEnum.THICKNESS_5*/}), - DIAGONAL_WALL("effortlessbuilding.mode.diagonal_wall", new DiagonalWall(), new ActionEnum[]{/*ActionEnum.FULL, ActionEnum.HOLLOW*/}), - SLOPE_FLOOR("effortlessbuilding.mode.slope_floor", new SlopeFloor(), new ActionEnum[]{ActionEnum.SHORT_EDGE, ActionEnum.LONG_EDGE}), - CUBE("effortlessbuilding.mode.cube", new Cube(), new ActionEnum[]{ActionEnum.CUBE_FULL, ActionEnum.CUBE_HOLLOW, ActionEnum.CUBE_SKELETON}); + //Uses a network message to get the previous raytraceresult from the player + //The server could keep track of all raytraceresults but this might lag with many players + //Raytraceresult is needed for sideHit and hitVec + public static void onBlockPlacedMessage(Player player, BlockPlacedMessage message) { - public String name; - public IBuildMode instance; - public ActionEnum[] options; + //Check if not in the middle of breaking + Dictionary currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer; + if (currentlyBreaking.get(player) != null && currentlyBreaking.get(player)) { + //Cancel breaking + initializeMode(player); + return; + } - BuildModeEnum(String name, IBuildMode instance, ActionEnum[] options) { - this.name = name; - this.instance = instance; - this.options = options; - } - } + ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); + ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); + BuildModeEnum buildMode = modeSettings.getBuildMode(); - //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(EntityPlayer player, BlockPlacedMessage message) { + BlockPos startPos = null; - //Check if not in the middle of breaking - Dictionary currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer; - if (currentlyBreaking.get(player) != null && currentlyBreaking.get(player)) { - //Cancel breaking - initializeMode(player); - return; - } + if (message.isBlockHit() && message.getBlockPos() != null) { + startPos = message.getBlockPos(); - ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); - BuildModeEnum buildMode = modeSettings.getBuildMode(); + //Offset in direction of sidehit if not quickreplace and not replaceable + //TODO 1.13 replaceable + boolean replaceable = player.level.getBlockState(startPos).getMaterial().isReplaceable(); + boolean becomesDoubleSlab = SurvivalHelper.doesBecomeDoubleSlab(player, startPos, message.getSideHit()); + if (!modifierSettings.doQuickReplace() && !replaceable && !becomesDoubleSlab) { + startPos = startPos.relative(message.getSideHit()); + } - BlockPos startPos = null; + //Get under tall grass and other replaceable blocks + if (modifierSettings.doQuickReplace() && replaceable) { + startPos = startPos.below(); + } - if (message.isBlockHit() && message.getBlockPos() != null) { - startPos = message.getBlockPos(); + //Check if player reach does not exceed startpos + int maxReach = ReachHelper.getMaxReach(player); + if (buildMode != BuildModeEnum.NORMAL && player.blockPosition().distSqr(startPos) > maxReach * maxReach) { + EffortlessBuilding.log(player, "Placement exceeds your reach."); + return; + } + } - //Offset in direction of sidehit if not quickreplace and not replaceable - boolean replaceable = player.world.getBlockState(startPos).getBlock().isReplaceable(player.world, startPos); - boolean becomesDoubleSlab = SurvivalHelper.doesBecomeDoubleSlab(player, startPos, message.getSideHit()); - if (!modifierSettings.doQuickReplace() && !replaceable && !becomesDoubleSlab) { - startPos = startPos.offset(message.getSideHit()); - } + //Even when no starting block is found, call buildmode instance + //We might want to place things in the air + List coordinates = buildMode.instance.onRightClick(player, startPos, message.getSideHit(), message.getHitVec(), modifierSettings.doQuickReplace()); - //Get under tall grass and other replaceable blocks - if (modifierSettings.doQuickReplace() && replaceable) { - startPos = startPos.down(); - } + if (coordinates.isEmpty()) { + currentlyBreaking.put(player, false); + return; + } - //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; - } - } + //Limit number of blocks you can place + int limit = ReachHelper.getMaxBlocksPlacedAtOnce(player); + if (coordinates.size() > limit) { + coordinates = coordinates.subList(0, limit); + } - //Even when no starting block is found, call buildmode instance - //We might want to place things in the air - List coordinates = buildMode.instance.onRightClick(player, startPos, message.getSideHit(), message.getHitVec(), modifierSettings.doQuickReplace()); + Direction sideHit = buildMode.instance.getSideHit(player); + if (sideHit == null) sideHit = message.getSideHit(); - if (coordinates.isEmpty()) { - currentlyBreaking.put(player, false); - return; - } + Vec3 hitVec = buildMode.instance.getHitVec(player); + if (hitVec == null) hitVec = message.getHitVec(); - //Limit number of blocks you can place - int limit = ReachHelper.getMaxBlocksPlacedAtOnce(player); - if (coordinates.size() > limit) { - coordinates = coordinates.subList(0, limit); - } + BuildModifiers.onBlockPlaced(player, coordinates, sideHit, hitVec, message.getPlaceStartPos()); - EnumFacing sideHit = buildMode.instance.getSideHit(player); - if (sideHit == null) sideHit = message.getSideHit(); + //Only works when finishing a buildmode is equal to placing some blocks + //No intermediate blocks allowed + currentlyBreaking.remove(player); - Vec3d hitVec = buildMode.instance.getHitVec(player); - if (hitVec == null) hitVec = message.getHitVec(); + } - BuildModifiers.onBlockPlaced(player, coordinates, sideHit, hitVec, message.getPlaceStartPos()); + //Use a network message to break blocks in the distance using clientside mouse input + public static void onBlockBrokenMessage(Player player, BlockBrokenMessage message) { + BlockPos startPos = message.isBlockHit() ? message.getBlockPos() : null; + onBlockBroken(player, startPos, true); + } - //Only works when finishing a buildmode is equal to placing some blocks - //No intermediate blocks allowed - currentlyBreaking.remove(player); + public static void onBlockBroken(Player player, BlockPos startPos, boolean breakStartPos) { - } + //Check if not in the middle of placing + Dictionary currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer; + if (currentlyBreaking.get(player) != null && !currentlyBreaking.get(player)) { + //Cancel placing + initializeMode(player); + return; + } - //Use a network message to break blocks in the distance using clientside mouse input - public static void onBlockBrokenMessage(EntityPlayer player, BlockBrokenMessage message) { - BlockPos startPos = message.isBlockHit() ? message.getBlockPos() : null; - onBlockBroken(player, startPos, true); - } + if (!ReachHelper.canBreakFar(player)) return; - public static void onBlockBroken(EntityPlayer player, BlockPos startPos, boolean breakStartPos) { + //If first click + if (currentlyBreaking.get(player) == null) { + //If startpos is null, dont do anything + if (startPos == null) return; + } - //Check if not in the middle of placing - Dictionary currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer; - if (currentlyBreaking.get(player) != null && !currentlyBreaking.get(player)) { - //Cancel placing - initializeMode(player); - return; - } + ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); + ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); - //If first click - if (currentlyBreaking.get(player) == null) { - //If startpos is null, dont do anything - if (startPos == null) return; - } + //Get coordinates + BuildModeEnum buildMode = modeSettings.getBuildMode(); + List coordinates = buildMode.instance.onRightClick(player, startPos, Direction.UP, Vec3.ZERO, true); - ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); + if (coordinates.isEmpty()) { + currentlyBreaking.put(player, true); + return; + } - //Get coordinates - BuildModeEnum buildMode = modeSettings.getBuildMode(); - List coordinates = buildMode.instance.onRightClick(player, startPos, EnumFacing.UP, Vec3d.ZERO, true); + //Let buildmodifiers break blocks + BuildModifiers.onBlockBroken(player, coordinates, breakStartPos); - if (coordinates.isEmpty()) { - currentlyBreaking.put(player, true); - return; - } + //Only works when finishing a buildmode is equal to breaking some blocks + //No intermediate blocks allowed + currentlyBreaking.remove(player); + } - //Let buildmodifiers break blocks - BuildModifiers.onBlockBroken(player, coordinates, breakStartPos); + public static List findCoordinates(Player player, BlockPos startPos, boolean skipRaytrace) { + List coordinates = new ArrayList<>(); - //Only works when finishing a buildmode is equal to breaking some blocks - //No intermediate blocks allowed - currentlyBreaking.remove(player); - } + ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); + coordinates.addAll(modeSettings.getBuildMode().instance.findCoordinates(player, startPos, skipRaytrace)); - public static List findCoordinates(EntityPlayer player, BlockPos startPos, boolean skipRaytrace) { - List coordinates = new ArrayList<>(); + return coordinates; + } - ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); - coordinates.addAll(modeSettings.getBuildMode().instance.findCoordinates(player, startPos, skipRaytrace)); + public static void initializeMode(Player player) { + //Resetting mode, so not placing or breaking + Dictionary currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer; + currentlyBreaking.remove(player); - return coordinates; - } + ModeSettingsManager.getModeSettings(player).getBuildMode().instance.initialize(player); + } - public static void initializeMode(EntityPlayer player) { - //Resetting mode, so not placing or breaking - Dictionary currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer; - currentlyBreaking.remove(player); + public static boolean isCurrentlyPlacing(Player player) { + Dictionary currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer; + return currentlyBreaking.get(player) != null && !currentlyBreaking.get(player); + } - ModeSettingsManager.getModeSettings(player).getBuildMode().instance.initialize(player); - } + public static boolean isCurrentlyBreaking(Player player) { + Dictionary currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer; + return currentlyBreaking.get(player) != null && currentlyBreaking.get(player); + } - public static boolean isCurrentlyPlacing(EntityPlayer player) { - Dictionary currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer; - return currentlyBreaking.get(player) != null && !currentlyBreaking.get(player); - } + //Either placing or breaking + public static boolean isActive(Player player) { + Dictionary currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer; + return currentlyBreaking.get(player) != null; + } - public static boolean isCurrentlyBreaking(EntityPlayer player) { - Dictionary currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer; - return currentlyBreaking.get(player) != null && currentlyBreaking.get(player); - } + //Find coordinates on a line bound by a plane + public static Vec3 findXBound(double x, Vec3 start, Vec3 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; - //Either placing or breaking - public static boolean isActive(EntityPlayer player) { - Dictionary currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer; - return currentlyBreaking.get(player) != null; - } + return new Vec3(x, y, z); + } - //Find coordinates on a line bound by a plane - 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; + //-- Common build mode functionality --// - return new Vec3d(x, y, z); - } + public static Vec3 findYBound(double y, Vec3 start, Vec3 look) { + //then x and z are + double x = (y - start.y) / look.y * look.x + start.x; + double z = (y - start.y) / look.y * look.z + start.z; - 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) { + //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; - 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(); + double x = lookVec.x; + double y = lookVec.y; + double z = lookVec.z; + //Further calculations (findXBound etc) don't like any component being 0 or 1 (e.g. dividing by 0) + //isCriteriaValid below will take up to 2 minutes to raytrace blocks towards infinity if that is the case + //So make sure they are close to but never exactly 0 or 1 + if (Math.abs(x) < 0.0001) x = 0.0001; + if (Math.abs(x - 1.0) < 0.0001) x = 0.9999; + if (Math.abs(x + 1.0) < 0.0001) x = -0.9999; + + if (Math.abs(y) < 0.0001) y = 0.0001; + if (Math.abs(y - 1.0) < 0.0001) y = 0.9999; + if (Math.abs(y + 1.0) < 0.0001) y = -0.9999; + + if (Math.abs(z) < 0.0001) z = 0.0001; + 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); + } + + public static boolean isCriteriaValid(Vec3 start, Vec3 look, int reach, Player player, boolean skipRaytrace, Vec3 lineBound, Vec3 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; + } + + return planeBound.subtract(start).dot(look) > 0 && + distToPlayerSq > 2 && distToPlayerSq < reach * reach && + !intersects; + } + + public enum BuildModeEnum { + NORMAL("normal", new Normal(), BuildModeCategoryEnum.BASIC), + NORMAL_PLUS("normal_plus", new NormalPlus(), BuildModeCategoryEnum.BASIC, OptionEnum.BUILD_SPEED), + LINE("line", new Line(), BuildModeCategoryEnum.BASIC /*, OptionEnum.THICKNESS*/), + WALL("wall", new Wall(), BuildModeCategoryEnum.BASIC, OptionEnum.FILL), + FLOOR("floor", new Floor(), BuildModeCategoryEnum.BASIC, OptionEnum.FILL), + CUBE("cube", new Cube(), BuildModeCategoryEnum.BASIC, OptionEnum.CUBE_FILL), + DIAGONAL_LINE("diagonal_line", new DiagonalLine(), BuildModeCategoryEnum.DIAGONAL /*, OptionEnum.THICKNESS*/), + DIAGONAL_WALL("diagonal_wall", new DiagonalWall(), BuildModeCategoryEnum.DIAGONAL /*, OptionEnum.FILL*/), + SLOPE_FLOOR("slope_floor", new SlopeFloor(), BuildModeCategoryEnum.DIAGONAL, OptionEnum.RAISED_EDGE), + CIRCLE("circle", new Circle(), BuildModeCategoryEnum.CIRCULAR, OptionEnum.CIRCLE_START, OptionEnum.FILL), + CYLINDER("cylinder", new Cylinder(), BuildModeCategoryEnum.CIRCULAR, OptionEnum.CIRCLE_START, OptionEnum.FILL), + SPHERE("sphere", new Sphere(), BuildModeCategoryEnum.CIRCULAR, OptionEnum.CIRCLE_START, OptionEnum.FILL); +// PYRAMID("pyramid", new Pyramid(), BuildModeCategoryEnum.ROOF), +// CONE("cone", new Cone(), BuildModeCategoryEnum.ROOF), +// DOME("dome", new Dome(), BuildModeCategoryEnum.ROOF); + + private final String name; + public final IBuildMode instance; + public final BuildModeCategoryEnum category; + public final OptionEnum[] options; + + BuildModeEnum(String name, IBuildMode instance, BuildModeCategoryEnum category, OptionEnum... options) { + this.name = name; + this.instance = instance; + this.category = category; + this.options = options; + } + + public String getNameKey() { + return "effortlessbuilding.mode." + name; + } + + public String getDescriptionKey() { + return "effortlessbuilding.modedescription." + name; + } + } + + public enum BuildModeCategoryEnum { + BASIC(new Vector4f(0f, .5f, 1f, .8f)), + DIAGONAL(new Vector4f(0.56f, 0.28f, 0.87f, .8f)), + CIRCULAR(new Vector4f(0.29f, 0.76f, 0.3f, 1f)), + ROOF(new Vector4f(0.83f, 0.87f, 0.23f, .8f)); + + public final Vector4f color; + + BuildModeCategoryEnum(Vector4f color) { + this.color = color; + } + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/Cube.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/Cube.java deleted file mode 100644 index 12258bd..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/Cube.java +++ /dev/null @@ -1,208 +0,0 @@ -package nl.requios.effortlessbuilding.buildmode; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import nl.requios.effortlessbuilding.helper.ReachHelper; - -import java.util.*; - -public class Cube implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - private Dictionary rightClickClientTable = new Hashtable<>(); - private Dictionary rightClickServerTable = new Hashtable<>(); - private Dictionary firstPosTable = new Hashtable<>(); - private Dictionary secondPosTable = new Hashtable<>(); - private Dictionary sideHitTable = new Hashtable<>(); - private Dictionary hitVecTable = new Hashtable<>(); - - @Override - public void initialize(EntityPlayer player) { - rightClickClientTable.put(player.getUniqueID(), 0); - rightClickServerTable.put(player.getUniqueID(), 0); - firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); - sideHitTable.put(player.getUniqueID(), EnumFacing.UP); - hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - - Dictionary 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; - } - - //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 = Floor.findFloor(player, firstPos, skipRaytrace); - - if (secondPos == null) { - rightClickTable.put(player.getUniqueID(), 1); - return list; - } - - secondPosTable.put(player.getUniqueID(), secondPos); - - } else { - //Third click, place cube with height - list = findCoordinates(player, blockPos, skipRaytrace); - rightClickTable.put(player.getUniqueID(), 0); - } - - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - - if (rightClickNr == 0) { - if (blockPos != null) - list.add(blockPos); - } else if (rightClickNr == 1) { - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - - BlockPos secondPos = Floor.findFloor(player, firstPos, skipRaytrace); - if (secondPos == null) return list; - - //Add whole floor - //Limit amount of blocks you can place per row - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - int x1 = firstPos.getX(), x2 = secondPos.getX(); - int y = firstPos.getY(); - int z1 = firstPos.getZ(), z2 = secondPos.getZ(); - - //limit axis - if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; - if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; - if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; - if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; - - if (ModeOptions.getCubeFill() == ModeOptions.ActionEnum.CUBE_SKELETON) { - //Hollow floor - Line.addXLineBlocks(list, x1, x2, y, z1); - Line.addXLineBlocks(list, x1, x2, y, z2); - Line.addZLineBlocks(list, z1, z2, x1, y); - Line.addZLineBlocks(list, z1, z2, x2, y); - } else { - //Filled floor - for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { - - for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { - - list.add(new BlockPos(l, y, n)); - } - } - } - - } else { - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - BlockPos secondPos = secondPosTable.get(player.getUniqueID()); - - BlockPos thirdPos = DiagonalLine.findHeight(player, secondPos, skipRaytrace); - if (thirdPos == null) return list; - - //Add whole cube - //Limit amount of blocks you can place per row - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - int x1 = firstPos.getX(), x2 = thirdPos.getX(); - int y1 = firstPos.getY(), y2 = thirdPos.getY(); - int z1 = firstPos.getZ(), z2 = thirdPos.getZ(); - - //limit axis - if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; - if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; - if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1; - if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1; - if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; - if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; - - switch (ModeOptions.getCubeFill()) { - case CUBE_FULL: - addCubeBlocks(list, x1, x2, y1, y2, z1, z2); - break; - case CUBE_HOLLOW: - addHollowCubeBlocks(list, x1, x2, y1, y2, z1, z2); - break; - case CUBE_SKELETON: - addSkeletonCubeBlocks(list, x1, x2, y1, y2, z1, z2); - break; - } - - } - - return list; - } - - public static void addCubeBlocks(List list, int x1, int x2, int y1, int y2, int z1, int z2) { - for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { - - for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { - - for (int m = y1; y1 < y2 ? m <= y2 : m >= y2; m += y1 < y2 ? 1 : -1) { - list.add(new BlockPos(l, m, n)); - } - } - } - } - - public static void addHollowCubeBlocks(List list, int x1, int x2, int y1, int y2, int z1, int z2) { - Wall.addXWallBlocks(list, x1, y1, y2, z1, z2); - Wall.addXWallBlocks(list, x2, y1, y2, z1, z2); - - Wall.addZWallBlocks(list, x1, x2, y1, y2, z1); - Wall.addZWallBlocks(list, x1, x2, y1, y2, z2); - - Floor.addFloorBlocks(list, x1, x2, y1, z1, z2); - Floor.addFloorBlocks(list, x1, x2, y2, z1, z2); - } - - public static void addSkeletonCubeBlocks(List list, int x1, int x2, int y1, int y2, int z1, int z2) { - Line.addXLineBlocks(list, x1, x2, y1, z1); - Line.addXLineBlocks(list, x1, x2, y1, z2); - Line.addXLineBlocks(list, x1, x2, y2, z1); - Line.addXLineBlocks(list, x1, x2, y2, z2); - - Line.addYLineBlocks(list, y1, y2, x1, z1); - Line.addYLineBlocks(list, y1, y2, x1, z2); - Line.addYLineBlocks(list, y1, y2, x2, z1); - Line.addYLineBlocks(list, y1, y2, x2, z2); - - Line.addZLineBlocks(list, z1, z2, x1, y1); - Line.addZLineBlocks(list, z1, z2, x1, y2); - Line.addZLineBlocks(list, z1, z2, x2, y1); - Line.addZLineBlocks(list, z1, z2, x2, y2); - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return sideHitTable.get(player.getUniqueID()); - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return hitVecTable.get(player.getUniqueID()); - } - -} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/DiagonalLine.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/DiagonalLine.java deleted file mode 100644 index 734d251..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/DiagonalLine.java +++ /dev/null @@ -1,228 +0,0 @@ -package nl.requios.effortlessbuilding.buildmode; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; -import nl.requios.effortlessbuilding.helper.ReachHelper; - -import java.util.*; - -public class DiagonalLine implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - private Dictionary rightClickClientTable = new Hashtable<>(); - private Dictionary rightClickServerTable = new Hashtable<>(); - private Dictionary firstPosTable = new Hashtable<>(); - private Dictionary secondPosTable = new Hashtable<>(); - private Dictionary sideHitTable = new Hashtable<>(); - private Dictionary hitVecTable = new Hashtable<>(); - - 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, EntityPlayer player, boolean skipRaytrace) { - - boolean intersects = false; - if (!skipRaytrace) { - //collision within a 1 block radius to selected is fine - RayTraceResult rayTraceResult = player.world.rayTraceBlocks(start, lineBound, false, true, false); - intersects = rayTraceResult != null && rayTraceResult.typeOfHit == RayTraceResult.Type.BLOCK && - planeBound.subtract(rayTraceResult.hitVec).lengthSquared() > 4; - } - - return planeBound.subtract(start).dotProduct(look) > 0 && - distToPlayerSq > 2 && distToPlayerSq < reach * reach && - !intersects; - } - } - - @Override - public void initialize(EntityPlayer player) { - rightClickClientTable.put(player.getUniqueID(), 0); - rightClickServerTable.put(player.getUniqueID(), 0); - firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); - sideHitTable.put(player.getUniqueID(), EnumFacing.UP); - hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - - Dictionary 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; - } - - //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 = Floor.findFloor(player, firstPos, true); - - if (secondPos == null) { - rightClickTable.put(player.getUniqueID(), 1); - return list; - } - - secondPosTable.put(player.getUniqueID(), secondPos); - - } else { - //Third click, place diagonal line with height - list = findCoordinates(player, blockPos, skipRaytrace); - rightClickTable.put(player.getUniqueID(), 0); - } - - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - - if (rightClickNr == 0) { - if (blockPos != null) - list.add(blockPos); - } else if (rightClickNr == 1) { - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - - BlockPos secondPos = Floor.findFloor(player, firstPos, true); - if (secondPos == null) return list; - - //Add diagonal line from first to second - list.addAll(getDiagonalLineBlocks(player , firstPos, secondPos, 10)); - - } else { - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - BlockPos secondPos = secondPosTable.get(player.getUniqueID()); - - BlockPos thirdPos = findHeight(player, secondPos, skipRaytrace); - if (thirdPos == null) return list; - - //Add diagonal line from first to third - list.addAll(getDiagonalLineBlocks(player , firstPos, thirdPos, 10)); - } - - return list; - } - - //Finds height after floor has been chosen in buildmodes with 3 clicks - public static BlockPos findHeight(EntityPlayer player, BlockPos secondPos, boolean skipRaytrace) { - Vec3d look = player.getLookVec(); - Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ); - - List criteriaList = new ArrayList<>(3); - - //X - Vec3d xBound = BuildModes.findXBound(secondPos.getX(), start, look); - criteriaList.add(new HeightCriteria(xBound, secondPos, start)); - - //Z - Vec3d zBound = BuildModes.findZBound(secondPos.getZ(), start, look); - criteriaList.add(new HeightCriteria(zBound, secondPos, start)); - - //Remove invalid criteria - 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 - if (criteriaList.isEmpty()) return null; - - //If only 1 is valid, choose that one - HeightCriteria selected = criteriaList.get(0); - - //If multiple are valid, choose based on criteria - if (criteriaList.size() > 1) { - //Select the one that is closest (from wall position to its line counterpart) - for (int i = 1; i < criteriaList.size(); i++) { - HeightCriteria criteria = criteriaList.get(i); - if (criteria.distToLineSq < 2.0 && selected.distToLineSq < 2.0) { - //Both very close to line, choose closest to player - if (criteria.distToPlayerSq < selected.distToPlayerSq) - selected = criteria; - } else { - //Pick closest to line - if (criteria.distToLineSq < selected.distToLineSq) - selected = criteria; - } - } - } - return new BlockPos(selected.lineBound); - } - - //Add diagonal line from first to second - public static List getDiagonalLineBlocks(EntityPlayer player, BlockPos firstPos, BlockPos secondPos, float sampleMultiplier) { - List list = new ArrayList<>(); - - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - int x1 = firstPos.getX(), x2 = secondPos.getX(); - int y1 = firstPos.getY(), y2 = secondPos.getY(); - int z1 = firstPos.getZ(), z2 = secondPos.getZ(); - - //limit axis - if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; - if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; - if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1; - if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1; - if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; - if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; - - 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) { - 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); - } - - return list; - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return sideHitTable.get(player.getUniqueID()); - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return hitVecTable.get(player.getUniqueID()); - } -} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/DiagonalWall.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/DiagonalWall.java deleted file mode 100644 index 7ba351c..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/DiagonalWall.java +++ /dev/null @@ -1,138 +0,0 @@ -package nl.requios.effortlessbuilding.buildmode; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import nl.requios.effortlessbuilding.helper.ReachHelper; - -import java.util.*; - -public class DiagonalWall implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - private Dictionary rightClickClientTable = new Hashtable<>(); - private Dictionary rightClickServerTable = new Hashtable<>(); - private Dictionary firstPosTable = new Hashtable<>(); - private Dictionary secondPosTable = new Hashtable<>(); - private Dictionary sideHitTable = new Hashtable<>(); - private Dictionary hitVecTable = new Hashtable<>(); - - @Override - public void initialize(EntityPlayer player) { - rightClickClientTable.put(player.getUniqueID(), 0); - rightClickServerTable.put(player.getUniqueID(), 0); - firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); - sideHitTable.put(player.getUniqueID(), EnumFacing.UP); - hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - - Dictionary 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; - } - - //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 = Floor.findFloor(player, firstPos, true); - - if (secondPos == null) { - rightClickTable.put(player.getUniqueID(), 1); - return list; - } - - secondPosTable.put(player.getUniqueID(), secondPos); - - } else { - //Third click, place diagonal wall with height - list = findCoordinates(player, blockPos, skipRaytrace); - rightClickTable.put(player.getUniqueID(), 0); - } - - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - - if (rightClickNr == 0) { - if (blockPos != null) - list.add(blockPos); - } else if (rightClickNr == 1) { - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - - BlockPos secondPos = Floor.findFloor(player, firstPos, true); - if (secondPos == null) return list; - - //Add diagonal line - list.addAll(DiagonalLine.getDiagonalLineBlocks(player, firstPos, secondPos, 1)); - - } else { - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - BlockPos secondPos = secondPosTable.get(player.getUniqueID()); - - BlockPos thirdPos = DiagonalLine.findHeight(player, secondPos, skipRaytrace); - if (thirdPos == null) return list; - - //Add diagonal wall - list.addAll(getDiagonalWallBlocks(player, firstPos, secondPos, thirdPos)); - } - - return list; - } - - //Add diagonal wall from first to second - public static List getDiagonalWallBlocks(EntityPlayer player, BlockPos firstPos, BlockPos secondPos, BlockPos thirdPos) { - List list = new ArrayList<>(); - - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - //Get diagonal line blocks - List diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, firstPos, secondPos, 1); - - //Limit amount of blocks we can place - int lowest = Math.min(firstPos.getY(), thirdPos.getY()); - int highest = Math.max(firstPos.getY(), thirdPos.getY()); - - if (highest - lowest >= axisLimit) highest = lowest + axisLimit - 1; - - //Copy diagonal line on y axis - for (int y = lowest; y <= highest; y++) { - for (BlockPos blockPos : diagonalLineBlocks) { - list.add(new BlockPos(blockPos.getX(), y, blockPos.getZ())); - } - } - - return list; - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return sideHitTable.get(player.getUniqueID()); - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return hitVecTable.get(player.getUniqueID()); - } -} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/Floor.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/Floor.java deleted file mode 100644 index 92aabbb..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/Floor.java +++ /dev/null @@ -1,183 +0,0 @@ -package nl.requios.effortlessbuilding.buildmode; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; -import nl.requios.effortlessbuilding.helper.ReachHelper; - -import java.util.*; - -public class Floor implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - private Dictionary rightClickClientTable = new Hashtable<>(); - private Dictionary rightClickServerTable = new Hashtable<>(); - private Dictionary firstPosTable = new Hashtable<>(); - private Dictionary sideHitTable = new Hashtable<>(); - private Dictionary hitVecTable = new Hashtable<>(); - - 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, EntityPlayer player, boolean skipRaytrace) { - - boolean intersects = false; - if (!skipRaytrace) { - //collision within a 1 block radius to selected is fine - RayTraceResult rayTraceResult = player.world.rayTraceBlocks(start, planeBound, false, true, false); - intersects = rayTraceResult != null && rayTraceResult.typeOfHit == RayTraceResult.Type.BLOCK && - planeBound.subtract(rayTraceResult.hitVec).lengthSquared() > 4; - } - - return planeBound.subtract(start).dotProduct(look) > 0 && - distToPlayerSq > 2 && distToPlayerSq < reach * reach && - !intersects; - } - } - - @Override - public void initialize(EntityPlayer player) { - rightClickClientTable.put(player.getUniqueID(), 0); - rightClickServerTable.put(player.getUniqueID(), 0); - firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); - sideHitTable.put(player.getUniqueID(), EnumFacing.UP); - hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - - Dictionary 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; - } - - //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 { - //Second click, place wall - - list = findCoordinates(player, blockPos, skipRaytrace); - rightClickTable.put(player.getUniqueID(), 0); - } - - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - - if (rightClickNr == 0) { - if (blockPos != null) - list.add(blockPos); - } else { - BlockPos secondPos = findFloor(player, firstPos, skipRaytrace); - if (secondPos == null) return list; - - //Add whole floor - list.addAll(getFloorBlocks(player, firstPos, secondPos)); - } - - return list; - } - - public static BlockPos findFloor(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { - Vec3d look = player.getLookVec(); - Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ); - - List criteriaList = new ArrayList<>(3); - - //Y - Vec3d yBound = BuildModes.findYBound(firstPos.getY(), start, look); - criteriaList.add(new Criteria(yBound, start)); - - //Remove invalid criteria - 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 - if (criteriaList.isEmpty()) return null; - - //Then only 1 can be valid, return that one - Criteria selected = criteriaList.get(0); - - return new BlockPos(selected.planeBound); - } - - public static List getFloorBlocks(EntityPlayer player, BlockPos firstPos, BlockPos secondPos) { - List list = new ArrayList<>(); - - //Limit amount of blocks you can place per row - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - int x1 = firstPos.getX(), x2 = secondPos.getX(); - int y = firstPos.getY(); - int z1 = firstPos.getZ(), z2 = secondPos.getZ(); - - //limit axis - if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; - if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; - if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; - if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; - - if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) - addFloorBlocks(list, x1, x2, y, z1, z2); - else - addHollowFloorBlocks(list, x1, x2, y, z1, z2); - - return list; - } - - public static void addFloorBlocks(List list, int x1, int x2, int y, int z1, int z2) { - - for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { - - for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { - - list.add(new BlockPos(l, y, n)); - } - } - } - - public static void addHollowFloorBlocks(List list, int x1, int x2, int y, int z1, int z2) { - Line.addXLineBlocks(list, x1, x2, y, z1); - Line.addXLineBlocks(list, x1, x2, y, z2); - Line.addZLineBlocks(list, z1, z2, x1, y); - Line.addZLineBlocks(list, z1, z2, x2, y); - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return sideHitTable.get(player.getUniqueID()); - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return hitVecTable.get(player.getUniqueID()); - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/IBuildMode.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/IBuildMode.java index f455641..dd5a82a 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/IBuildMode.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/IBuildMode.java @@ -1,25 +1,25 @@ package nl.requios.effortlessbuilding.buildmode; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.Direction; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; import java.util.List; public interface IBuildMode { - //Fired when a player selects a buildmode and when it needs to initializeMode - void initialize(EntityPlayer player); + //Fired when a player selects a buildmode and when it needs to initializeMode + void initialize(Player player); - //Fired when a block would be placed - //Return a list of coordinates where you want to place blocks - List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace); + //Fired when a block would be placed + //Return a list of coordinates where you want to place blocks + List onRightClick(Player player, BlockPos blockPos, Direction sideHit, Vec3 hitVec, boolean skipRaytrace); - //Fired continuously for visualization purposes - List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace); + //Fired continuously for visualization purposes + List findCoordinates(Player player, BlockPos blockPos, boolean skipRaytrace); - EnumFacing getSideHit(EntityPlayer player); + Direction getSideHit(Player player); - Vec3d getHitVec(EntityPlayer player); + Vec3 getHitVec(Player player); } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/Line.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/Line.java deleted file mode 100644 index 2da9bc1..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/Line.java +++ /dev/null @@ -1,239 +0,0 @@ -package nl.requios.effortlessbuilding.buildmode; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; -import nl.requios.effortlessbuilding.helper.ReachHelper; - -import java.util.*; - -public class Line implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - private Dictionary rightClickClientTable = new Hashtable<>(); - private Dictionary rightClickServerTable = new Hashtable<>(); - private Dictionary firstPosTable = new Hashtable<>(); - private Dictionary sideHitTable = new Hashtable<>(); - private Dictionary hitVecTable = new Hashtable<>(); - - 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, EntityPlayer player, boolean skipRaytrace) { - - boolean intersects = false; - if (!skipRaytrace) { - //collision within a 1 block radius to selected is fine - RayTraceResult rayTraceResult = player.world.rayTraceBlocks(start, lineBound, false, true, false); - intersects = rayTraceResult != null && rayTraceResult.typeOfHit == RayTraceResult.Type.BLOCK && - planeBound.subtract(rayTraceResult.hitVec).lengthSquared() > 4; - } - - return planeBound.subtract(start).dotProduct(look) > 0 && - distToPlayerSq > 2 && distToPlayerSq < reach * reach && - !intersects; - } - } - - @Override - public void initialize(EntityPlayer player) { - rightClickClientTable.put(player.getUniqueID(), 0); - rightClickServerTable.put(player.getUniqueID(), 0); - firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); - sideHitTable.put(player.getUniqueID(), EnumFacing.UP); - hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - - Dictionary 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; - } - - //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 { - //Second click, place wall - - list = findCoordinates(player, blockPos, skipRaytrace); - rightClickTable.put(player.getUniqueID(), 0); - } - - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - - if (rightClickNr == 0) { - if (blockPos != null) - list.add(blockPos); - } else { - BlockPos secondPos = findLine(player, firstPos, skipRaytrace); - if (secondPos == null) return list; - - list.addAll(getLineBlocks(player, firstPos, secondPos)); - } - - return list; - } - - public static BlockPos findLine(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { - Vec3d look = player.getLookVec(); - Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ); - - List criteriaList = new ArrayList<>(3); - - //X - Vec3d xBound = BuildModes.findXBound(firstPos.getX(), start, look); - criteriaList.add(new Criteria(xBound, firstPos, start)); - - //Y - Vec3d yBound = BuildModes.findYBound(firstPos.getY(), start, look); - criteriaList.add(new Criteria(yBound, firstPos, start)); - - //Z - Vec3d zBound = BuildModes.findZBound(firstPos.getZ(), start, look); - criteriaList.add(new Criteria(zBound, firstPos, start)); - - //Remove invalid criteria - 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 - if (criteriaList.isEmpty()) return null; - - //If only 1 is valid, choose that one - Criteria selected = criteriaList.get(0); - - //If multiple are valid, choose based on criteria - if (criteriaList.size() > 1) { - //Select the one that is closest (from wall position to its line counterpart) - for (int i = 1; i < criteriaList.size(); i++) { - Criteria criteria = criteriaList.get(i); - if (criteria.distToLineSq < 2.0 && selected.distToLineSq < 2.0) { - //Both very close to line, choose closest to player - if (criteria.distToPlayerSq < selected.distToPlayerSq) - selected = criteria; - } else { - //Pick closest to line - if (criteria.distToLineSq < selected.distToLineSq) - selected = criteria; - } - } - - } - - return new BlockPos(selected.lineBound); - } - - public static List getLineBlocks(EntityPlayer player, BlockPos firstPos, BlockPos secondPos) { - List list = new ArrayList<>(); - - //Limit amount of blocks we can place - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - //Add whole line - int x1 = firstPos.getX(), x2 = secondPos.getX(); - int y1 = firstPos.getY(), y2 = secondPos.getY(); - int z1 = firstPos.getZ(), z2 = secondPos.getZ(); - - //limit axis - if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; - if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; - if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1; - if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1; - if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; - if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; - - if (x1 != x2) { - addXLineBlocks(list, x1, x2, y1, z1); - } else if (y1 != y2) { - addYLineBlocks(list, y1, y2, x1, z1); - } else { - addZLineBlocks(list, z1, z2, x1, y1); - } - - return list; - } - - public static void addXLineBlocks(List list, int x1, int x2, int y, int z) { - for (int x = x1; x1 < x2 ? x <= x2 : x >= x2; x += x1 < x2 ? 1 : -1) { - list.add(new BlockPos(x, y, z)); - } - } - - public static void addYLineBlocks(List list, int y1, int y2, int x, int z) { - for (int y = y1; y1 < y2 ? y <= y2 : y >= y2; y += y1 < y2 ? 1 : -1) { - list.add(new BlockPos(x, y, z)); - } - } - - public static void addZLineBlocks(List list, int z1, int z2, int x, int y) { - for (int z = z1; z1 < z2 ? z <= z2 : z >= z2; z += z1 < z2 ? 1 : -1) { - list.add(new BlockPos(x, y, z)); - } - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return sideHitTable.get(player.getUniqueID()); - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return hitVecTable.get(player.getUniqueID()); - } - -} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java index e82ea70..101c6d5 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java @@ -1,7 +1,7 @@ package nl.requios.effortlessbuilding.buildmode; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.text.TextFormatting; +import net.minecraft.world.entity.player.Player; +import net.minecraft.ChatFormatting; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; import nl.requios.effortlessbuilding.buildmodifier.UndoRedo; @@ -9,124 +9,183 @@ import nl.requios.effortlessbuilding.proxy.ClientProxy; 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"), + private static ActionEnum buildSpeed = ActionEnum.NORMAL_SPEED; + private static ActionEnum fill = ActionEnum.FULL; + private static ActionEnum cubeFill = ActionEnum.CUBE_FULL; + private static ActionEnum raisedEdge = ActionEnum.SHORT_EDGE; + private static ActionEnum lineThickness = ActionEnum.THICKNESS_1; + private static ActionEnum circleStart = ActionEnum.CIRCLE_START_CORNER; - NORMAL_SPEED("effortlessbuilding.action.normal_speed"), - FAST_SPEED("effortlessbuilding.action.fast_speed"), + public static ActionEnum getOptionSetting(OptionEnum option) { + switch (option) { + case BUILD_SPEED: + return getBuildSpeed(); + case FILL: + return getFill(); + case CUBE_FILL: + return getCubeFill(); + case RAISED_EDGE: + return getRaisedEdge(); + case LINE_THICKNESS: + return getLineThickness(); + case CIRCLE_START: + return getCircleStart(); + default: + return null; + } + } - FULL("effortlessbuilding.action.full"), - HOLLOW("effortlessbuilding.action.hollow"), + public static ActionEnum getBuildSpeed() { + return buildSpeed; + } - CUBE_FULL("effortlessbuilding.action.full"), - CUBE_HOLLOW("effortlessbuilding.action.hollow"), - CUBE_SKELETON("effortlessbuilding.action.skeleton"), + public static ActionEnum getFill() { + return fill; + } - SHORT_EDGE("effortlessbuilding.action.short_edge"), - LONG_EDGE("effortlessbuilding.action.long_edge"), + public static ActionEnum getCubeFill() { + return cubeFill; + } - THICKNESS_1("effortlessbuilding.action.thickness_1"), - THICKNESS_3("effortlessbuilding.action.thickness_3"), - THICKNESS_5("effortlessbuilding.action.thickness_5"); + public static ActionEnum getRaisedEdge() { + return raisedEdge; + } - public String name; + public static ActionEnum getLineThickness() { + return lineThickness; + } - ActionEnum(String name) { - this.name = name; - } - } + public static ActionEnum getCircleStart() { + return circleStart; + } - private static ActionEnum buildSpeed = ActionEnum.NORMAL_SPEED; - private static ActionEnum fill = ActionEnum.FULL; - private static ActionEnum cubeFill = ActionEnum.CUBE_FULL; - private static ActionEnum raisedEdge = ActionEnum.SHORT_EDGE; - private static ActionEnum lineThickness = ActionEnum.THICKNESS_1; + //Called on both client and server + public static void performAction(Player player, ActionEnum action) { + if (action == null) return; - public static ActionEnum getBuildSpeed() { - return buildSpeed; - } + switch (action) { + case UNDO: + UndoRedo.undo(player); + break; + case REDO: + UndoRedo.redo(player); + break; + case REPLACE: + ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); + modifierSettings.setQuickReplace(!modifierSettings.doQuickReplace()); + EffortlessBuilding.log(player, "Set " + ChatFormatting.GOLD + "Quick Replace " + ChatFormatting.RESET + ( + modifierSettings.doQuickReplace() ? "on" : "off"), true); + break; + case OPEN_MODIFIER_SETTINGS: + if (player.level.isClientSide) + ClientProxy.openModifierSettings(); + break; + case OPEN_PLAYER_SETTINGS: + if (player.level.isClientSide) + ClientProxy.openPlayerSettings(); + break; - public static ActionEnum getFill() { - return fill; - } + 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; + } - public static ActionEnum getCubeFill() { - return cubeFill; - } + if (player.level.isClientSide && + action != ActionEnum.REPLACE && + action != ActionEnum.OPEN_MODIFIER_SETTINGS && + action != ActionEnum.OPEN_PLAYER_SETTINGS) { - public static ActionEnum getRaisedEdge() { - return raisedEdge; - } + EffortlessBuilding.logTranslate(player, "", action.name, "", true); + } + } - public static ActionEnum getLineThickness() { - return lineThickness; - } + public enum ActionEnum { + UNDO("effortlessbuilding.action.undo"), + REDO("effortlessbuilding.action.redo"), + REPLACE("effortlessbuilding.action.replace"), + OPEN_MODIFIER_SETTINGS("effortlessbuilding.action.open_modifier_settings"), + OPEN_PLAYER_SETTINGS("effortlessbuilding.action.open_player_settings"), - //Called on both client and server - public static void performAction(EntityPlayer player, ActionEnum action) { - if (action == null) return; + NORMAL_SPEED("effortlessbuilding.action.normal_speed"), + FAST_SPEED("effortlessbuilding.action.fast_speed"), - switch (action) { - case UNDO: - UndoRedo.undo(player); - break; - case REDO: - UndoRedo.redo(player); - break; - case REPLACE: - ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - modifierSettings.setQuickReplace(!modifierSettings.doQuickReplace()); - EffortlessBuilding.log(player, "Set " + TextFormatting.GOLD + "Quick Replace " + TextFormatting.RESET + ( - modifierSettings.doQuickReplace() ? "on" : "off"), true); - break; - case OPEN_MODIFIER_SETTINGS: - if (player.world.isRemote) - ClientProxy.openModifierSettings(); - break; + FULL("effortlessbuilding.action.full"), + HOLLOW("effortlessbuilding.action.hollow"), - 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; - } + CUBE_FULL("effortlessbuilding.action.full"), + CUBE_HOLLOW("effortlessbuilding.action.hollow"), + CUBE_SKELETON("effortlessbuilding.action.skeleton"), - if (player.world.isRemote && action != ActionEnum.REPLACE && action != ActionEnum.OPEN_MODIFIER_SETTINGS) { - ClientProxy.logTranslate(action.name); - } - } + 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; + } + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeSettingsManager.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeSettingsManager.java index 4bc38c3..264939c 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeSettingsManager.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeSettingsManager.java @@ -1,86 +1,97 @@ package nl.requios.effortlessbuilding.buildmode; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.world.entity.player.Player; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.network.PacketDistributor; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.capability.ModeCapabilityManager; import nl.requios.effortlessbuilding.helper.ReachHelper; import nl.requios.effortlessbuilding.network.ModeSettingsMessage; +import nl.requios.effortlessbuilding.network.PacketHandler; + +import javax.annotation.Nonnull; @Mod.EventBusSubscriber public class ModeSettingsManager { - //Retrieves the buildsettings of a player through the modifierCapability capability - //Never returns null - public static ModeSettings getModeSettings(EntityPlayer player) { - if (player.hasCapability(ModeCapabilityManager.modeCapability, null)) { - ModeCapabilityManager.IModeCapability capability = player.getCapability( - ModeCapabilityManager.modeCapability, null); - if (capability.getModeData() == null) { - capability.setModeData(new ModeSettings()); - } - return capability.getModeData(); - } - throw new IllegalArgumentException("Player does not have modeCapability capability"); - } + //Retrieves the buildsettings of a player through the modeCapability capability + //Never returns null + @Nonnull + public static ModeSettings getModeSettings(Player player) { + LazyOptional modeCapability = + player.getCapability(ModeCapabilityManager.MODE_CAPABILITY, null); - public static void setModeSettings(EntityPlayer player, ModeSettings modeSettings) { - if (player == null) { - EffortlessBuilding.log("Cannot set buildsettings, player is null"); - return; - } - if (player.hasCapability(ModeCapabilityManager.modeCapability, null)) { - ModeCapabilityManager.IModeCapability capability = 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(); + } - capability.setModeData(modeSettings); + EffortlessBuilding.logger.warn("Player does not have modeCapability: " + player); + //Return dummy settings + return new ModeSettings(); + } - //Initialize new mode - BuildModes.initializeMode(player); - } else { - EffortlessBuilding.log(player, "Saving buildsettings failed."); - } - } + public static void setModeSettings(Player player, ModeSettings modeSettings) { + if (player == null) { + EffortlessBuilding.log("Cannot set buildmode settings, player is null"); + return; + } + LazyOptional modeCapability = + player.getCapability(ModeCapabilityManager.MODE_CAPABILITY, null); - public static String sanitize(ModeSettings modeSettings, EntityPlayer player) { - int maxReach = ReachHelper.getMaxReach(player); - String error = ""; + modeCapability.ifPresent((capability) -> { + capability.setModeData(modeSettings); - //TODO sanitize + BuildModes.initializeMode(player); + }); - return error; - } + if (!modeCapability.isPresent()) { + EffortlessBuilding.log(player, "Saving buildmode settings failed."); + } + } - public static class ModeSettings { - private BuildModes.BuildModeEnum buildMode = BuildModes.BuildModeEnum.NORMAL; + public static String sanitize(ModeSettings modeSettings, Player player) { + int maxReach = ReachHelper.getMaxReach(player); + String error = ""; - public ModeSettings() { - } + //TODO sanitize - public ModeSettings(BuildModes.BuildModeEnum buildMode) { - this.buildMode = buildMode; - } + return error; + } - public BuildModes.BuildModeEnum getBuildMode() { - return this.buildMode; - } + public static void handleNewPlayer(Player player) { + //Makes sure player has mode settings (if it doesnt it will create it) + getModeSettings(player); - public void setBuildMode(BuildModes.BuildModeEnum buildMode) { - this.buildMode = buildMode; - } - } + //Only on server + if (!player.level.isClientSide) { + //Send to client + ModeSettingsMessage msg = new ModeSettingsMessage(getModeSettings(player)); + PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), msg); + } + } - public static void handleNewPlayer(EntityPlayer player){ - if (getModeSettings(player) == null) { - setModeSettings(player, new ModeSettings()); - } + public static class ModeSettings { + private BuildModes.BuildModeEnum buildMode = BuildModes.BuildModeEnum.NORMAL; - //Only on server - if (!player.world.isRemote) { - //Send to client - ModeSettingsMessage msg = new ModeSettingsMessage(getModeSettings(player)); - EffortlessBuilding.packetHandler.sendTo(msg, (EntityPlayerMP) player); - } - } + 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; + } + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/Normal.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/Normal.java deleted file mode 100644 index 430cb1b..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/Normal.java +++ /dev/null @@ -1,40 +0,0 @@ -package nl.requios.effortlessbuilding.buildmode; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; - -import java.util.ArrayList; -import java.util.List; - -public class Normal implements IBuildMode { - @Override - public void initialize(EntityPlayer player) { - - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - if (blockPos != null) list.add(blockPos); - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - if (blockPos != null) list.add(blockPos); - return list; - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return null; - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return null; - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/NormalPlus.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/NormalPlus.java deleted file mode 100644 index a503efa..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/NormalPlus.java +++ /dev/null @@ -1,40 +0,0 @@ -package nl.requios.effortlessbuilding.buildmode; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; - -import java.util.ArrayList; -import java.util.List; - -public class NormalPlus implements IBuildMode { - @Override - public void initialize(EntityPlayer player) { - - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - if (blockPos != null) list.add(blockPos); - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - if (blockPos != null) list.add(blockPos); - return list; - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return null; - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return null; - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/SlopeFloor.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/SlopeFloor.java deleted file mode 100644 index 35fa01a..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/SlopeFloor.java +++ /dev/null @@ -1,178 +0,0 @@ -package nl.requios.effortlessbuilding.buildmode; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import nl.requios.effortlessbuilding.helper.ReachHelper; - -import java.util.*; - -public class SlopeFloor implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - private Dictionary rightClickClientTable = new Hashtable<>(); - private Dictionary rightClickServerTable = new Hashtable<>(); - private Dictionary firstPosTable = new Hashtable<>(); - private Dictionary secondPosTable = new Hashtable<>(); - private Dictionary sideHitTable = new Hashtable<>(); - private Dictionary hitVecTable = new Hashtable<>(); - - @Override - public void initialize(EntityPlayer player) { - rightClickClientTable.put(player.getUniqueID(), 0); - rightClickServerTable.put(player.getUniqueID(), 0); - firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); - sideHitTable.put(player.getUniqueID(), EnumFacing.UP); - hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - - Dictionary 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; - } - - //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 = Floor.findFloor(player, firstPos, true); - - if (secondPos == null) { - rightClickTable.put(player.getUniqueID(), 1); - return list; - } - - secondPosTable.put(player.getUniqueID(), secondPos); - - } else { - //Third click, place slope floor with height - list = findCoordinates(player, blockPos, skipRaytrace); - rightClickTable.put(player.getUniqueID(), 0); - } - - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - - if (rightClickNr == 0) { - if (blockPos != null) - list.add(blockPos); - } else if (rightClickNr == 1) { - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - - BlockPos secondPos = Floor.findFloor(player, firstPos, true); - if (secondPos == null) return list; - - //Add whole floor - list.addAll(Floor.getFloorBlocks(player, firstPos, secondPos)); - - } else { - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - BlockPos secondPos = secondPosTable.get(player.getUniqueID()); - - BlockPos thirdPos = DiagonalLine.findHeight(player, secondPos, skipRaytrace); - if (thirdPos == null) return list; - - //Add slope floor blocks - list.addAll(getSlopeFloorBlocks(player, firstPos, secondPos, thirdPos)); - } - - return list; - } - - //Add slope floor from first to second - public static List getSlopeFloorBlocks(EntityPlayer player, BlockPos firstPos, BlockPos secondPos, BlockPos thirdPos) { - List list = new ArrayList<>(); - - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - //Determine whether to use x or z axis to slope up - boolean onXAxis = true; - - int xLength = Math.abs(secondPos.getX() - firstPos.getX()); - int zLength = Math.abs(secondPos.getZ() - firstPos.getZ()); - - if (ModeOptions.getRaisedEdge() == ModeOptions.ActionEnum.SHORT_EDGE) { - //Slope along short edge - if (zLength > xLength) onXAxis = false; - } else { - //Slope along long edge - if (zLength <= xLength) onXAxis = false; - } - - if (onXAxis) { - //Along X goes up - - //Get diagonal line blocks - BlockPos linePoint = new BlockPos(secondPos.getX(), thirdPos.getY(), firstPos.getZ()); - List diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, firstPos, linePoint, 1f); - - //Limit amount of blocks we can place - int lowest = Math.min(firstPos.getZ(), secondPos.getZ()); - int highest = Math.max(firstPos.getZ(), secondPos.getZ()); - - if (highest - lowest >= axisLimit) highest = lowest + axisLimit - 1; - - //Copy diagonal line on x axis - for (int z = lowest; z <= highest; z++) { - for (BlockPos blockPos : diagonalLineBlocks) { - list.add(new BlockPos(blockPos.getX(), blockPos.getY(), z)); - } - } - - } else { - //Along Z goes up - - //Get diagonal line blocks - BlockPos linePoint = new BlockPos(firstPos.getX(), thirdPos.getY(), secondPos.getZ()); - List diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, firstPos, linePoint, 1f); - - //Limit amount of blocks we can place - int lowest = Math.min(firstPos.getX(), secondPos.getX()); - int highest = Math.max(firstPos.getX(), secondPos.getX()); - - if (highest - lowest >= axisLimit) highest = lowest + axisLimit - 1; - - //Copy diagonal line on x axis - for (int x = lowest; x <= highest; x++) { - for (BlockPos blockPos : diagonalLineBlocks) { - list.add(new BlockPos(x, blockPos.getY(), blockPos.getZ())); - } - } - } - - - return list; - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return sideHitTable.get(player.getUniqueID()); - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return hitVecTable.get(player.getUniqueID()); - } -} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/ThreeClicksBuildMode.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/ThreeClicksBuildMode.java new file mode 100644 index 0000000..25cd050 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/ThreeClicksBuildMode.java @@ -0,0 +1,213 @@ +package nl.requios.effortlessbuilding.buildmode; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.Direction; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; +import nl.requios.effortlessbuilding.helper.ReachHelper; + +import java.util.*; + +public abstract class ThreeClicksBuildMode extends BaseBuildMode { + protected Dictionary secondPosTable = new Hashtable<>(); + + //Finds height after floor has been chosen in buildmodes with 3 clicks + 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()); + + List criteriaList = new ArrayList<>(3); + + //X + Vec3 xBound = BuildModes.findXBound(secondPos.getX(), start, look); + criteriaList.add(new HeightCriteria(xBound, secondPos, start)); + + //Z + Vec3 zBound = BuildModes.findZBound(secondPos.getZ(), start, look); + criteriaList.add(new HeightCriteria(zBound, secondPos, start)); + + //Remove invalid criteria + 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 + if (criteriaList.isEmpty()) return null; + + //If only 1 is valid, choose that one + HeightCriteria selected = criteriaList.get(0); + + //If multiple are valid, choose based on criteria + if (criteriaList.size() > 1) { + //Select the one that is closest (from wall position to its line counterpart) + for (int i = 1; i < criteriaList.size(); i++) { + HeightCriteria criteria = criteriaList.get(i); + if (criteria.distToLineSq < 2.0 && selected.distToLineSq < 2.0) { + //Both very close to line, choose closest to player + if (criteria.distToPlayerSq < selected.distToPlayerSq) + selected = criteria; + } else { + //Pick closest to line + if (criteria.distToLineSq < selected.distToLineSq) + selected = criteria; + } + } + } + return new BlockPos(selected.lineBound); + } + + @Override + public void initialize(Player player) { + super.initialize(player); + secondPosTable.put(player.getUUID(), BlockPos.ZERO); + } + + @Override + public List onRightClick(Player player, BlockPos blockPos, Direction sideHit, Vec3 hitVec, boolean skipRaytrace) { + List list = new ArrayList<>(); + + Dictionary rightClickTable = player.level.isClientSide ? rightClickClientTable : rightClickServerTable; + int rightClickNr = rightClickTable.get(player.getUUID()); + rightClickNr++; + rightClickTable.put(player.getUUID(), rightClickNr); + + if (rightClickNr == 1) { + //If clicking in air, reset and try again + if (blockPos == null) { + rightClickTable.put(player.getUUID(), 0); + return list; + } + + //First click, remember starting position + firstPosTable.put(player.getUUID(), blockPos); + sideHitTable.put(player.getUUID(), sideHit); + hitVecTable.put(player.getUUID(), hitVec); + //Keep list empty, dont place any blocks yet + } else if (rightClickNr == 2) { + //Second click, find other floor point + BlockPos firstPos = firstPosTable.get(player.getUUID()); + BlockPos secondPos = findSecondPos(player, firstPos, true); + + if (secondPos == null) { + rightClickTable.put(player.getUUID(), 1); + return list; + } + + secondPosTable.put(player.getUUID(), secondPos); + + } else { + //Third click, place diagonal wall with height + list = findCoordinates(player, blockPos, skipRaytrace); + rightClickTable.put(player.getUUID(), 0); + } + + return list; + } + + @Override + public List findCoordinates(Player player, BlockPos blockPos, boolean skipRaytrace) { + List list = new ArrayList<>(); + Dictionary rightClickTable = player.level.isClientSide ? rightClickClientTable : rightClickServerTable; + int rightClickNr = rightClickTable.get(player.getUUID()); + + if (rightClickNr == 0) { + if (blockPos != null) + list.add(blockPos); + } else if (rightClickNr == 1) { + BlockPos firstPos = firstPosTable.get(player.getUUID()); + + BlockPos secondPos = findSecondPos(player, firstPos, true); + if (secondPos == null) return list; + + //Limit amount of blocks you can place per row + int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); + + int x1 = firstPos.getX(), x2 = secondPos.getX(); + int y1 = firstPos.getY(), y2 = secondPos.getY(); + int z1 = firstPos.getZ(), z2 = secondPos.getZ(); + + //limit axis + if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; + if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; + if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1; + if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1; + if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; + if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; + + //Add diagonal line from first to second + list.addAll(getIntermediateBlocks(player, x1, y1, z1, x2, y2, z2)); + + } else { + BlockPos firstPos = firstPosTable.get(player.getUUID()); + BlockPos secondPos = secondPosTable.get(player.getUUID()); + + BlockPos thirdPos = findThirdPos(player, firstPos, secondPos, skipRaytrace); + if (thirdPos == null) return list; + + //Limit amount of blocks you can place per row + int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); + + int x1 = firstPos.getX(), x2 = secondPos.getX(), x3 = thirdPos.getX(); + int y1 = firstPos.getY(), y2 = secondPos.getY(), y3 = thirdPos.getY(); + int z1 = firstPos.getZ(), z2 = secondPos.getZ(), z3 = thirdPos.getZ(); + + //limit axis + if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; + if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; + if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1; + if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1; + if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; + if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; + + if (x3 - x1 >= axisLimit) x3 = x1 + axisLimit - 1; + if (x1 - x3 >= axisLimit) x3 = x1 - axisLimit + 1; + if (y3 - y1 >= axisLimit) y3 = y1 + axisLimit - 1; + if (y1 - y3 >= axisLimit) y3 = y1 - axisLimit + 1; + if (z3 - z1 >= axisLimit) z3 = z1 + axisLimit - 1; + if (z1 - z3 >= axisLimit) z3 = z1 - axisLimit + 1; + + //Add diagonal line from first to third + list.addAll(getFinalBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3)); + } + + return list; + } + + //Finds the place of the second block pos + 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 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 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 = new BlockPos(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); + } + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/TwoClicksBuildMode.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/TwoClicksBuildMode.java new file mode 100644 index 0000000..8d3bf87 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/TwoClicksBuildMode.java @@ -0,0 +1,86 @@ +package nl.requios.effortlessbuilding.buildmode; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.Direction; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; +import 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 { + + @Override + public List onRightClick(Player player, BlockPos blockPos, Direction sideHit, Vec3 hitVec, boolean skipRaytrace) { + List list = new ArrayList<>(); + + Dictionary rightClickTable = player.level.isClientSide ? rightClickClientTable : rightClickServerTable; + int rightClickNr = rightClickTable.get(player.getUUID()); + rightClickNr++; + rightClickTable.put(player.getUUID(), rightClickNr); + + if (rightClickNr == 1) { + //If clicking in air, reset and try again + if (blockPos == null) { + rightClickTable.put(player.getUUID(), 0); + return list; + } + + //First click, remember starting position + firstPosTable.put(player.getUUID(), blockPos); + sideHitTable.put(player.getUUID(), sideHit); + hitVecTable.put(player.getUUID(), hitVec); + //Keep list empty, dont place any blocks yet + } else { + //Second click, place blocks + list = findCoordinates(player, blockPos, skipRaytrace); + rightClickTable.put(player.getUUID(), 0); + } + + return list; + } + + @Override + public List findCoordinates(Player player, BlockPos blockPos, boolean skipRaytrace) { + List list = new ArrayList<>(); + Dictionary rightClickTable = player.level.isClientSide ? rightClickClientTable : rightClickServerTable; + int rightClickNr = rightClickTable.get(player.getUUID()); + BlockPos firstPos = firstPosTable.get(player.getUUID()); + + if (rightClickNr == 0) { + if (blockPos != null) + list.add(blockPos); + } else { + BlockPos secondPos = findSecondPos(player, firstPos, skipRaytrace); + if (secondPos == null) return list; + + //Limit amount of blocks we can place per row + int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); + + int x1 = firstPos.getX(), x2 = secondPos.getX(); + int y1 = firstPos.getY(), y2 = secondPos.getY(); + int z1 = firstPos.getZ(), z2 = secondPos.getZ(); + + //limit axis + if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; + if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; + if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1; + if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1; + if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; + if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; + + list.addAll(getAllBlocks(player, x1, y1, z1, x2, y2, z2)); + } + + 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); + + //After first and second pos are known, we want all the blocks + protected abstract List getAllBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2); +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/Wall.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/Wall.java deleted file mode 100644 index efe6665..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/Wall.java +++ /dev/null @@ -1,226 +0,0 @@ -package nl.requios.effortlessbuilding.buildmode; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; -import nl.requios.effortlessbuilding.helper.ReachHelper; - -import java.util.*; - -public class Wall implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - private Dictionary rightClickClientTable = new Hashtable<>(); - private Dictionary rightClickServerTable = new Hashtable<>(); - private Dictionary firstPosTable = new Hashtable<>(); - private Dictionary sideHitTable = new Hashtable<>(); - private Dictionary hitVecTable = new Hashtable<>(); - - 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, EntityPlayer player, boolean skipRaytrace) { - - boolean intersects = false; - if (!skipRaytrace) { - //collision within a 1 block radius to selected is fine - RayTraceResult rayTraceResult = player.world.rayTraceBlocks(start, planeBound, false, true, false); - intersects = rayTraceResult != null && rayTraceResult.typeOfHit == RayTraceResult.Type.BLOCK && - planeBound.subtract(rayTraceResult.hitVec).lengthSquared() > 4; - } - - return planeBound.subtract(start).dotProduct(look) > 0 && - distToPlayerSq > 2 && distToPlayerSq < reach * reach && - !intersects; - } - } - - @Override - public void initialize(EntityPlayer player) { - rightClickClientTable.put(player.getUniqueID(), 0); - rightClickServerTable.put(player.getUniqueID(), 0); - firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); - sideHitTable.put(player.getUniqueID(), EnumFacing.UP); - hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - - Dictionary 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; - } - - //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 { - //Second click, place wall - - list = findCoordinates(player, blockPos, skipRaytrace); - rightClickTable.put(player.getUniqueID(), 0); - } - - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - - if (rightClickNr == 0) { - if (blockPos != null) - list.add(blockPos); - } else { - BlockPos secondPos = findWall(player, firstPos, skipRaytrace); - if (secondPos == null) return list; - - //Add whole wall - list.addAll(getWallBlocks(player, firstPos, secondPos)); - } - - return list; - } - - public static BlockPos findWall(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { - Vec3d look = player.getLookVec(); - Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ); - - List criteriaList = new ArrayList<>(3); - - //X - Vec3d xBound = BuildModes.findXBound(firstPos.getX(), start, look); - criteriaList.add(new Criteria(xBound, firstPos, start, look)); - - //Z - Vec3d zBound = BuildModes.findZBound(firstPos.getZ(), start, look); - criteriaList.add(new Criteria(zBound, firstPos, start, look)); - - //Remove invalid criteria - int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach - criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace)); - - //If none are valid, return empty list of blocks - if (criteriaList.isEmpty()) return null; - - //If only 1 is valid, choose that one - Criteria selected = criteriaList.get(0); - - //If multiple are valid, choose based on criteria - if (criteriaList.size() > 1) { - //Select the one that is closest - //Limit the angle to not be too extreme - for (int i = 1; i < criteriaList.size(); i++) { - Criteria criteria = criteriaList.get(i); - if (criteria.distToPlayerSq < selected.distToPlayerSq && Math.abs(criteria.angle) - Math.abs(selected.angle) < 3) - selected = criteria; - } - } - - return new BlockPos(selected.planeBound); - } - - public static List getWallBlocks(EntityPlayer player, BlockPos firstPos, BlockPos secondPos) { - List list = new ArrayList<>(); - - //Limit amount of blocks we can place per row - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - int x1 = firstPos.getX(), x2 = secondPos.getX(); - int y1 = firstPos.getY(), y2 = secondPos.getY(); - int z1 = firstPos.getZ(), z2 = secondPos.getZ(); - - //limit axis - if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; - if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; - if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1; - if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1; - if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; - if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; - - if (x1 == x2) { - if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) - addXWallBlocks(list, x1, y1, y2, z1, z2); - else - addXHollowWallBlocks(list, x1, y1, y2, z1, z2); - } else { - if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) - addZWallBlocks(list, x1, x2, y1, y2, z1); - else - addZHollowWallBlocks(list, x1, x2, y1, y2, z1); - } - - return list; - } - - public static void addXWallBlocks(List list, int x, int y1, int y2, int z1, int z2) { - - for (int z = z1; z1 < z2 ? z <= z2 : z >= z2; z += z1 < z2 ? 1 : -1) { - - for (int y = y1; y1 < y2 ? y <= y2 : y >= y2; y += y1 < y2 ? 1 : -1) { - list.add(new BlockPos(x, y, z)); - } - } - } - - public static void addZWallBlocks(List list, int x1, int x2, int y1, int y2, int z) { - - for (int x = x1; x1 < x2 ? x <= x2 : x >= x2; x += x1 < x2 ? 1 : -1) { - - for (int y = y1; y1 < y2 ? y <= y2 : y >= y2; y += y1 < y2 ? 1 : -1) { - list.add(new BlockPos(x, y, z)); - } - } - } - - public static void addXHollowWallBlocks(List list, int x, int y1, int y2, int z1, int z2) { - Line.addZLineBlocks(list, z1, z2, x, y1); - Line.addZLineBlocks(list, z1, z2, x, y2); - Line.addYLineBlocks(list, y1, y2, x, z1); - Line.addYLineBlocks(list, y1, y2, x, z2); - } - - public static void addZHollowWallBlocks(List list, int x1, int x2, int y1, int y2, int z) { - Line.addXLineBlocks(list, x1, x2, y1, z); - Line.addXLineBlocks(list, x1, x2, y2, z); - Line.addYLineBlocks(list, y1, y2, x1, z); - Line.addYLineBlocks(list, y1, y2, x2, z); - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return sideHitTable.get(player.getUniqueID()); - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return hitVecTable.get(player.getUniqueID()); - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Circle.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Circle.java new file mode 100644 index 0000000..9f72642 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Circle.java @@ -0,0 +1,89 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.BlockPos; +import net.minecraft.util.Mth; +import nl.requios.effortlessbuilding.buildmode.ModeOptions; +import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode; + +import java.util.ArrayList; +import java.util.List; + +public class Circle extends TwoClicksBuildMode { + + public static List getCircleBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) { + List list = new ArrayList<>(); + + float centerX = x1; + float centerZ = z1; + + //Adjust for CIRCLE_START + if (ModeOptions.getCircleStart() == ModeOptions.ActionEnum.CIRCLE_START_CORNER) { + centerX = x1 + (x2 - x1) / 2f; + centerZ = z1 + (z2 - z1) / 2f; + } else { + x1 = (int) (centerX - (x2 - centerX)); + z1 = (int) (centerZ - (z2 - centerZ)); + } + + float radiusX = Mth.abs(x2 - centerX); + float radiusZ = Mth.abs(z2 - centerZ); + + if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) + addCircleBlocks(list, x1, y1, z1, x2, y2, z2, centerX, centerZ, radiusX, radiusZ); + else + addHollowCircleBlocks(list, x1, y1, z1, x2, y2, z2, centerX, centerZ, radiusX, radiusZ); + + return list; + } + + public static void addCircleBlocks(List list, int x1, int y1, int z1, int x2, int y2, int z2, float centerX, float centerZ, float radiusX, float radiusZ) { + + for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { + + for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { + + float distance = distance(l, n, centerX, centerZ); + float radius = calculateEllipseRadius(centerX, centerZ, radiusX, radiusZ, l, n); + if (distance < radius + 0.4f) + list.add(new BlockPos(l, y1, n)); + } + } + } + + public static void addHollowCircleBlocks(List list, int x1, int y1, int z1, int x2, int y2, int z2, float centerX, float centerZ, float radiusX, float radiusZ) { + + for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { + + for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { + + float distance = distance(l, n, centerX, centerZ); + float radius = calculateEllipseRadius(centerX, centerZ, radiusX, radiusZ, l, n); + if (distance < radius + 0.4f && distance > radius - 0.6f) + list.add(new BlockPos(l, y1, n)); + } + } + } + + private static float distance(float x1, float z1, float x2, float z2) { + return Mth.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 getAllBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) { + return getCircleBlocks(player, x1, y1, z1, x2, y2, z2); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cone.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cone.java new file mode 100644 index 0000000..78b91a6 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cone.java @@ -0,0 +1,31 @@ +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 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 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); + } +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cube.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cube.java new file mode 100644 index 0000000..3a2fb1c --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cube.java @@ -0,0 +1,101 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.BlockPos; +import nl.requios.effortlessbuilding.buildmode.ModeOptions; +import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode; + +import java.util.ArrayList; +import java.util.List; + +public class Cube extends ThreeClicksBuildMode { + + public static List getFloorBlocksUsingCubeFill(Player player, int x1, int y1, int z1, int x2, int y2, int z2) { + List list = new ArrayList<>(); + + if (ModeOptions.getCubeFill() == ModeOptions.ActionEnum.CUBE_SKELETON) + Floor.addHollowFloorBlocks(list, x1, x2, y1, z1, z2); + else + Floor.addFloorBlocks(list, x1, x2, y1, z1, z2); + + return list; + } + + public static List getCubeBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) { + List list = new ArrayList<>(); + + switch (ModeOptions.getCubeFill()) { + case CUBE_FULL: + addCubeBlocks(list, x1, x2, y1, y2, z1, z2); + break; + case CUBE_HOLLOW: + addHollowCubeBlocks(list, x1, x2, y1, y2, z1, z2); + break; + case CUBE_SKELETON: + addSkeletonCubeBlocks(list, x1, x2, y1, y2, z1, z2); + break; + } + + return list; + } + + public static void addCubeBlocks(List list, int x1, int x2, int y1, int y2, int z1, int z2) { + for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { + + for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { + + for (int m = y1; y1 < y2 ? m <= y2 : m >= y2; m += y1 < y2 ? 1 : -1) { + list.add(new BlockPos(l, m, n)); + } + } + } + } + + public static void addHollowCubeBlocks(List list, int x1, int x2, int y1, int y2, int z1, int z2) { + Wall.addXWallBlocks(list, x1, y1, y2, z1, z2); + Wall.addXWallBlocks(list, x2, y1, y2, z1, z2); + + Wall.addZWallBlocks(list, x1, x2, y1, y2, z1); + Wall.addZWallBlocks(list, x1, x2, y1, y2, z2); + + Floor.addFloorBlocks(list, x1, x2, y1, z1, z2); + Floor.addFloorBlocks(list, x1, x2, y2, z1, z2); + } + + public static void addSkeletonCubeBlocks(List list, int x1, int x2, int y1, int y2, int z1, int z2) { + Line.addXLineBlocks(list, x1, x2, y1, z1); + Line.addXLineBlocks(list, x1, x2, y1, z2); + Line.addXLineBlocks(list, x1, x2, y2, z1); + Line.addXLineBlocks(list, x1, x2, y2, z2); + + Line.addYLineBlocks(list, y1, y2, x1, z1); + Line.addYLineBlocks(list, y1, y2, x1, z2); + Line.addYLineBlocks(list, y1, y2, x2, z1); + Line.addYLineBlocks(list, y1, y2, x2, z2); + + Line.addZLineBlocks(list, z1, z2, x1, y1); + Line.addZLineBlocks(list, z1, z2, x1, y2); + 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 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 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); + } +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cylinder.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cylinder.java new file mode 100644 index 0000000..1c303bf --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cylinder.java @@ -0,0 +1,50 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.BlockPos; +import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode; + +import java.util.ArrayList; +import java.util.List; + +public class Cylinder extends ThreeClicksBuildMode { + + public static List getCylinderBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) { + List list = new ArrayList<>(); + + //Get circle blocks (using CIRCLE_START and FILL options built-in) + List circleBlocks = Circle.getCircleBlocks(player, x1, y1, z1, x2, y2, z2); + + int lowest = Math.min(y1, y3); + int highest = Math.max(y1, y3); + + //Copy circle on y axis + for (int y = lowest; y <= highest; y++) { + for (BlockPos blockPos : circleBlocks) { + list.add(new BlockPos(blockPos.getX(), y, blockPos.getZ())); + } + } + + 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 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 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); + } +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/DiagonalLine.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/DiagonalLine.java new file mode 100644 index 0000000..25365f4 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/DiagonalLine.java @@ -0,0 +1,53 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; +import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode; + +import java.util.ArrayList; +import java.util.List; + +public class DiagonalLine extends ThreeClicksBuildMode { + + //Add diagonal line from first to second + public static List getDiagonalLineBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, float sampleMultiplier) { + List 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); + + int iterations = (int) Math.ceil(first.distanceTo(second) * sampleMultiplier); + for (double t = 0; t <= 1.0; t += 1.0 / iterations) { + Vec3 lerp = first.add(second.subtract(first).scale(t)); + BlockPos candidate = new BlockPos(lerp); + //Only add if not equal to the last in the list + if (list.isEmpty() || !list.get(list.size() - 1).equals(candidate)) + list.add(candidate); + } + + 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 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 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); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/DiagonalWall.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/DiagonalWall.java new file mode 100644 index 0000000..c2d039a --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/DiagonalWall.java @@ -0,0 +1,51 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.BlockPos; +import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode; + +import java.util.ArrayList; +import java.util.List; + +public class DiagonalWall extends ThreeClicksBuildMode { + + //Add diagonal wall from first to second + public static List getDiagonalWallBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) { + List list = new ArrayList<>(); + + //Get diagonal line blocks + List diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, x1, y1, z1, x2, y2, z2, 1); + + int lowest = Math.min(y1, y3); + int highest = Math.max(y1, y3); + + //Copy diagonal line on y axis + for (int y = lowest; y <= highest; y++) { + for (BlockPos blockPos : diagonalLineBlocks) { + list.add(new BlockPos(blockPos.getX(), y, blockPos.getZ())); + } + } + + 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 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 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); + } +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Dome.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Dome.java new file mode 100644 index 0000000..8845413 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Dome.java @@ -0,0 +1,31 @@ +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 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 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); + } +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Floor.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Floor.java new file mode 100644 index 0000000..97735cc --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Floor.java @@ -0,0 +1,94 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; +import nl.requios.effortlessbuilding.buildmode.BuildModes; +import nl.requios.effortlessbuilding.buildmode.ModeOptions; +import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode; +import nl.requios.effortlessbuilding.helper.ReachHelper; + +import java.util.ArrayList; +import java.util.List; + +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()); + + List criteriaList = new ArrayList<>(3); + + //Y + Vec3 yBound = BuildModes.findYBound(firstPos.getY(), start, look); + criteriaList.add(new Criteria(yBound, start)); + + //Remove invalid criteria + 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 + if (criteriaList.isEmpty()) return null; + + //Then only 1 can be valid, return that one + Criteria selected = criteriaList.get(0); + + return new BlockPos(selected.planeBound); + } + + public static List getFloorBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) { + List list = new ArrayList<>(); + + if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) + addFloorBlocks(list, x1, x2, y1, z1, z2); + else + addHollowFloorBlocks(list, x1, x2, y1, z1, z2); + + return list; + } + + public static void addFloorBlocks(List list, int x1, int x2, int y, int z1, int z2) { + + for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { + + for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { + + list.add(new BlockPos(l, y, n)); + } + } + } + + public static void addHollowFloorBlocks(List list, int x1, int x2, int y, int z1, int z2) { + Line.addXLineBlocks(list, x1, x2, y, z1); + Line.addXLineBlocks(list, x1, x2, y, z2); + 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 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); + } + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Line.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Line.java new file mode 100644 index 0000000..3b7aa33 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Line.java @@ -0,0 +1,147 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; +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()); + + List criteriaList = new ArrayList<>(3); + + //X + Vec3 xBound = BuildModes.findXBound(firstPos.getX(), start, look); + criteriaList.add(new Criteria(xBound, firstPos, start)); + + //Y + Vec3 yBound = BuildModes.findYBound(firstPos.getY(), start, look); + criteriaList.add(new Criteria(yBound, firstPos, start)); + + //Z + Vec3 zBound = BuildModes.findZBound(firstPos.getZ(), start, look); + criteriaList.add(new Criteria(zBound, firstPos, start)); + + //Remove invalid criteria + 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 + if (criteriaList.isEmpty()) return null; + + //If only 1 is valid, choose that one + Criteria selected = criteriaList.get(0); + + //If multiple are valid, choose based on criteria + if (criteriaList.size() > 1) { + //Select the one that is closest (from wall position to its line counterpart) + for (int i = 1; i < criteriaList.size(); i++) { + Criteria criteria = criteriaList.get(i); + if (criteria.distToLineSq < 2.0 && selected.distToLineSq < 2.0) { + //Both very close to line, choose closest to player + if (criteria.distToPlayerSq < selected.distToPlayerSq) + selected = criteria; + } else { + //Pick closest to line + if (criteria.distToLineSq < selected.distToLineSq) + selected = criteria; + } + } + + } + + return new BlockPos(selected.lineBound); + } + + public static List getLineBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) { + List list = new ArrayList<>(); + + if (x1 != x2) { + addXLineBlocks(list, x1, x2, y1, z1); + } else if (y1 != y2) { + addYLineBlocks(list, y1, y2, x1, z1); + } else { + addZLineBlocks(list, z1, z2, x1, y1); + } + + return list; + } + + public static void addXLineBlocks(List list, int x1, int x2, int y, int z) { + for (int x = x1; x1 < x2 ? x <= x2 : x >= x2; x += x1 < x2 ? 1 : -1) { + list.add(new BlockPos(x, y, z)); + } + } + + public static void addYLineBlocks(List list, int y1, int y2, int x, int z) { + for (int y = y1; y1 < y2 ? y <= y2 : y >= y2; y += y1 < y2 ? 1 : -1) { + list.add(new BlockPos(x, y, z)); + } + } + + public static void addZLineBlocks(List list, int z1, int z2, int x, int y) { + for (int z = z1; z1 < z2 ? z <= z2 : z >= z2; z += z1 < z2 ? 1 : -1) { + list.add(new BlockPos(x, y, z)); + } + } + + @Override + protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) { + return findLine(player, firstPos, skipRaytrace); + } + + @Override + protected List 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 = 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 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); + } + + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Normal.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Normal.java new file mode 100644 index 0000000..3f71149 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Normal.java @@ -0,0 +1,41 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.Direction; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; +import nl.requios.effortlessbuilding.buildmode.IBuildMode; + +import java.util.ArrayList; +import java.util.List; + +public class Normal implements IBuildMode { + @Override + public void initialize(Player player) { + + } + + @Override + public List onRightClick(Player player, BlockPos blockPos, Direction sideHit, Vec3 hitVec, boolean skipRaytrace) { + List list = new ArrayList<>(); + if (blockPos != null) list.add(blockPos); + return list; + } + + @Override + public List findCoordinates(Player player, BlockPos blockPos, boolean skipRaytrace) { + List list = new ArrayList<>(); + if (blockPos != null) list.add(blockPos); + return list; + } + + @Override + public Direction getSideHit(Player player) { + return null; + } + + @Override + public Vec3 getHitVec(Player player) { + return null; + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/NormalPlus.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/NormalPlus.java new file mode 100644 index 0000000..07c3403 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/NormalPlus.java @@ -0,0 +1,41 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.Direction; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; +import nl.requios.effortlessbuilding.buildmode.IBuildMode; + +import java.util.ArrayList; +import java.util.List; + +public class NormalPlus implements IBuildMode { + @Override + public void initialize(Player player) { + + } + + @Override + public List onRightClick(Player player, BlockPos blockPos, Direction sideHit, Vec3 hitVec, boolean skipRaytrace) { + List list = new ArrayList<>(); + if (blockPos != null) list.add(blockPos); + return list; + } + + @Override + public List findCoordinates(Player player, BlockPos blockPos, boolean skipRaytrace) { + List list = new ArrayList<>(); + if (blockPos != null) list.add(blockPos); + return list; + } + + @Override + public Direction getSideHit(Player player) { + return null; + } + + @Override + public Vec3 getHitVec(Player player) { + return null; + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Pyramid.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Pyramid.java new file mode 100644 index 0000000..25bb7d1 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Pyramid.java @@ -0,0 +1,34 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.player.Player; +import nl.requios.effortlessbuilding.buildmode.ModeOptions; +import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode; +import nl.requios.effortlessbuilding.helper.ReachHelper; + +import java.util.ArrayList; +import java.util.List; + +public class Pyramid extends ThreeClicksBuildMode { + + @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 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 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); + } +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/SlopeFloor.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/SlopeFloor.java new file mode 100644 index 0000000..b20a33f --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/SlopeFloor.java @@ -0,0 +1,95 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.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 { + + //Add slope floor from first to second + public static List getSlopeFloorBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) { + List list = new ArrayList<>(); + + int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); + + //Determine whether to use x or z axis to slope up + boolean onXAxis = true; + + int xLength = Math.abs(x2 - x1); + int zLength = Math.abs(z2 - z1); + + if (ModeOptions.getRaisedEdge() == ModeOptions.ActionEnum.SHORT_EDGE) { + //Slope along short edge + if (zLength > xLength) onXAxis = false; + } else { + //Slope along long edge + if (zLength <= xLength) onXAxis = false; + } + + if (onXAxis) { + //Along X goes up + + //Get diagonal line blocks + List diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, x1, y1, z1, x2, y3, z1, 1f); + + //Limit amount of blocks we can place + int lowest = Math.min(z1, z2); + int highest = Math.max(z1, z2); + + if (highest - lowest >= axisLimit) highest = lowest + axisLimit - 1; + + //Copy diagonal line on x axis + for (int z = lowest; z <= highest; z++) { + for (BlockPos blockPos : diagonalLineBlocks) { + list.add(new BlockPos(blockPos.getX(), blockPos.getY(), z)); + } + } + + } else { + //Along Z goes up + + //Get diagonal line blocks + List diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, x1, y1, z1, x1, y3, z2, 1f); + + //Limit amount of blocks we can place + int lowest = Math.min(x1, x2); + int highest = Math.max(x1, x2); + + if (highest - lowest >= axisLimit) highest = lowest + axisLimit - 1; + + //Copy diagonal line on x axis + for (int x = lowest; x <= highest; x++) { + for (BlockPos blockPos : diagonalLineBlocks) { + list.add(new BlockPos(x, blockPos.getY(), blockPos.getZ())); + } + } + } + + 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 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 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); + } +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Sphere.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Sphere.java new file mode 100644 index 0000000..0625b18 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Sphere.java @@ -0,0 +1,110 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.BlockPos; +import net.minecraft.util.Mth; +import nl.requios.effortlessbuilding.buildmode.ModeOptions; +import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode; + +import java.util.ArrayList; +import java.util.List; + +public class Sphere extends ThreeClicksBuildMode { + + public static List getSphereBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) { + List list = new ArrayList<>(); + + float centerX = x1; + float centerY = y1; + float centerZ = z1; + + //Adjust for CIRCLE_START + if (ModeOptions.getCircleStart() == ModeOptions.ActionEnum.CIRCLE_START_CORNER) { + centerX = x1 + (x2 - x1) / 2f; + centerY = y1 + (y3 - y1) / 2f; + centerZ = z1 + (z2 - z1) / 2f; + } else { + x1 = (int) (centerX - (x2 - centerX)); + y1 = (int) (centerY - (y3 - centerY)); + z1 = (int) (centerZ - (z2 - centerZ)); + } + + float radiusX = Mth.abs(x2 - centerX); + float radiusY = Mth.abs(y3 - centerY); + float radiusZ = Mth.abs(z2 - centerZ); + + if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) + addSphereBlocks(list, x1, y1, z1, x3, y3, z3, centerX, centerY, centerZ, radiusX, radiusY, radiusZ); + else + addHollowSphereBlocks(list, x1, y1, z1, x3, y3, z3, centerX, centerY, centerZ, radiusX, radiusY, radiusZ); + + return list; + } + + public static void addSphereBlocks(List list, int x1, int y1, int z1, int x2, int y2, int z2, + float centerX, float centerY, float centerZ, float radiusX, float radiusY, float radiusZ) { + for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { + + for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { + + for (int m = y1; y1 < y2 ? m <= y2 : m >= y2; m += y1 < y2 ? 1 : -1) { + + float distance = distance(l, m, n, centerX, centerY, centerZ); + float radius = calculateSpheroidRadius(centerX, centerY, centerZ, radiusX, radiusY, radiusZ, l, m, n); + if (distance < radius + 0.4f) + list.add(new BlockPos(l, m, n)); + } + } + } + } + + public static void addHollowSphereBlocks(List list, int x1, int y1, int z1, int x2, int y2, int z2, + float centerX, float centerY, float centerZ, float radiusX, float radiusY, float radiusZ) { + for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { + + for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { + + for (int m = y1; y1 < y2 ? m <= y2 : m >= y2; m += y1 < y2 ? 1 : -1) { + + float distance = distance(l, m, n, centerX, centerY, centerZ); + float radius = calculateSpheroidRadius(centerX, centerY, centerZ, radiusX, radiusY, radiusZ, l, m, n); + if (distance < radius + 0.4f && distance > radius - 0.6f) + list.add(new BlockPos(l, m, n)); + } + } + } + } + + 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)); + } + + public static float calculateSpheroidRadius(float centerX, float centerY, float centerZ, float radiusX, float radiusY, float radiusZ, int x, int y, int z) { + //Twice ellipse radius + float radiusXZ = Circle.calculateEllipseRadius(centerX, centerZ, radiusX, radiusZ, x, z); + + //TODO project x to plane + + 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 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 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); + } +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Wall.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Wall.java new file mode 100644 index 0000000..5f7e493 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Wall.java @@ -0,0 +1,135 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; +import nl.requios.effortlessbuilding.buildmode.BuildModes; +import nl.requios.effortlessbuilding.buildmode.ModeOptions; +import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode; +import nl.requios.effortlessbuilding.helper.ReachHelper; + +import java.util.ArrayList; +import java.util.List; + +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()); + + List criteriaList = new ArrayList<>(3); + + //X + Vec3 xBound = BuildModes.findXBound(firstPos.getX(), start, look); + criteriaList.add(new Criteria(xBound, firstPos, start, look)); + + //Z + Vec3 zBound = BuildModes.findZBound(firstPos.getZ(), start, look); + criteriaList.add(new Criteria(zBound, firstPos, start, look)); + + //Remove invalid criteria + int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach + criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace)); + + //If none are valid, return empty list of blocks + if (criteriaList.isEmpty()) return null; + + //If only 1 is valid, choose that one + Criteria selected = criteriaList.get(0); + + //If multiple are valid, choose based on criteria + if (criteriaList.size() > 1) { + //Select the one that is closest + //Limit the angle to not be too extreme + for (int i = 1; i < criteriaList.size(); i++) { + Criteria criteria = criteriaList.get(i); + if (criteria.distToPlayerSq < selected.distToPlayerSq && Math.abs(criteria.angle) - Math.abs(selected.angle) < 3) + selected = criteria; + } + } + + return new BlockPos(selected.planeBound); + } + + public static List getWallBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) { + List list = new ArrayList<>(); + + if (x1 == x2) { + if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) + addXWallBlocks(list, x1, y1, y2, z1, z2); + else + addXHollowWallBlocks(list, x1, y1, y2, z1, z2); + } else { + if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) + addZWallBlocks(list, x1, x2, y1, y2, z1); + else + addZHollowWallBlocks(list, x1, x2, y1, y2, z1); + } + + return list; + } + + public static void addXWallBlocks(List list, int x, int y1, int y2, int z1, int z2) { + + for (int z = z1; z1 < z2 ? z <= z2 : z >= z2; z += z1 < z2 ? 1 : -1) { + + for (int y = y1; y1 < y2 ? y <= y2 : y >= y2; y += y1 < y2 ? 1 : -1) { + list.add(new BlockPos(x, y, z)); + } + } + } + + public static void addZWallBlocks(List list, int x1, int x2, int y1, int y2, int z) { + + for (int x = x1; x1 < x2 ? x <= x2 : x >= x2; x += x1 < x2 ? 1 : -1) { + + for (int y = y1; y1 < y2 ? y <= y2 : y >= y2; y += y1 < y2 ? 1 : -1) { + list.add(new BlockPos(x, y, z)); + } + } + } + + public static void addXHollowWallBlocks(List list, int x, int y1, int y2, int z1, int z2) { + Line.addZLineBlocks(list, z1, z2, x, y1); + Line.addZLineBlocks(list, z1, z2, x, y2); + Line.addYLineBlocks(list, y1, y2, x, z1); + Line.addYLineBlocks(list, y1, y2, x, z2); + } + + public static void addZHollowWallBlocks(List list, int x1, int x2, int y1, int y2, int z) { + Line.addXLineBlocks(list, x1, x2, y1, z); + Line.addXLineBlocks(list, x1, x2, y2, z); + 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 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); + } + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/Array.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/Array.java index 6f0cc37..a90703f 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/Array.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/Array.java @@ -1,104 +1,104 @@ package nl.requios.effortlessbuilding.buildmodifier; -import net.minecraft.block.state.IBlockState; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.math.Vec3i; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.core.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; +import net.minecraft.core.Vec3i; import net.minecraftforge.items.IItemHandler; -import nl.requios.effortlessbuilding.item.ItemRandomizerBag; +import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem; import java.util.ArrayList; import java.util.List; public class Array { - public static class ArraySettings{ - public boolean enabled = false; - public BlockPos offset = BlockPos.ORIGIN; - public int count = 5; + public static List findCoordinates(Player player, BlockPos startPos) { + List coordinates = new ArrayList<>(); - public ArraySettings() { - } + //find arraysettings for the player + ArraySettings a = ModifierSettingsManager.getModifierSettings(player).getArraySettings(); + if (!isEnabled(a)) return coordinates; - public ArraySettings(boolean enabled, BlockPos offset, int count) { - this.enabled = enabled; - this.offset = offset; - this.count = count; - } + BlockPos pos = startPos; + Vec3i offset = new Vec3i(a.offset.getX(), a.offset.getY(), a.offset.getZ()); - public int getReach() { - //find largest offset - int x = Math.abs(offset.getX()); - int y = Math.abs(offset.getY()); - int z = Math.abs(offset.getZ()); - int largestOffset = Math.max(Math.max(x, y), z); + for (int i = 0; i < a.count; i++) { + pos = pos.offset(offset); + coordinates.add(pos); + } - return largestOffset * count; - } - } + return coordinates; + } - public static List findCoordinates(EntityPlayer player, BlockPos startPos) { - List coordinates = new ArrayList<>(); + public static List findBlockStates(Player player, BlockPos startPos, BlockState blockState, ItemStack itemStack, List itemStacks) { + List blockStates = new ArrayList<>(); - //find arraysettings for the player - ArraySettings a = ModifierSettingsManager.getModifierSettings(player).getArraySettings(); - if (!isEnabled(a)) return coordinates; + //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()); + 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); - } + //Randomizer bag synergy + AbstractRandomizerBagItem randomizerBagItem = null; + IItemHandler bagInventory = null; + if (!itemStack.isEmpty() && itemStack.getItem() instanceof AbstractRandomizerBagItem) { + randomizerBagItem = (AbstractRandomizerBagItem) itemStack.getItem() ; + bagInventory = randomizerBagItem.getBagInventory(itemStack); + } - return coordinates; - } + for (int i = 0; i < a.count; i++) { + pos = pos.offset(offset); - public static List findBlockStates(EntityPlayer player, BlockPos startPos, IBlockState blockState, ItemStack itemStack, List itemStacks) { - List blockStates = new ArrayList<>(); + //Randomizer bag synergy + if (randomizerBagItem != null) { + itemStack = randomizerBagItem.pickRandomStack(bagInventory); + blockState = BuildModifiers + .getBlockStateFromItem(itemStack, player, startPos, Direction.UP, new Vec3(0, 0, 0), InteractionHand.MAIN_HAND); + } - //find arraysettings for the player that placed the block - ArraySettings a = ModifierSettingsManager.getModifierSettings(player).getArraySettings(); - if (!isEnabled(a)) return blockStates; + //blockState = blockState.getBlock().getStateForPlacement(player.world, pos, ) + blockStates.add(blockState); + itemStacks.add(itemStack); + } - BlockPos pos = startPos; - Vec3i offset = new Vec3i(a.offset.getX(), a.offset.getY(), a.offset.getZ()); + return blockStates; + } - //Randomizer bag synergy - IItemHandler bagInventory = null; - if (!itemStack.isEmpty() && itemStack.getItem() instanceof ItemRandomizerBag) { - bagInventory = ItemRandomizerBag.getBagInventory(itemStack); - } + public static boolean isEnabled(ArraySettings a) { + if (a == null || !a.enabled) return false; - for (int i = 0; i < a.count; i++) { - pos = pos.add(offset); + return a.offset.getX() != 0 || a.offset.getY() != 0 || a.offset.getZ() != 0; + } - //Randomizer bag synergy - if (bagInventory != null) { - itemStack = ItemRandomizerBag.pickRandomStack(bagInventory); - blockState = BuildModifiers - .getBlockStateFromItem(itemStack, player, startPos, EnumFacing.UP, new Vec3d(0, 0, 0), EnumHand.MAIN_HAND); - } + public static class ArraySettings { + public boolean enabled = false; + public BlockPos offset = BlockPos.ZERO; + public int count = 5; - //blockState = blockState.getBlock().getStateForPlacement(player.world, pos, ) - blockStates.add(blockState); - itemStacks.add(itemStack); - } + public ArraySettings() { + } - return blockStates; - } + public ArraySettings(boolean enabled, BlockPos offset, int count) { + this.enabled = enabled; + this.offset = offset; + this.count = count; + } - public static boolean isEnabled(ArraySettings a) { - if (a == null || !a.enabled) return false; + public int getReach() { + //find largest offset + int x = Math.abs(offset.getX()); + int y = Math.abs(offset.getY()); + int z = Math.abs(offset.getZ()); + int largestOffset = Math.max(Math.max(x, y), z); - if (a.offset.getX() == 0 && a.offset.getY() == 0 && a.offset.getZ() == 0) return false; - - return true; - } + return largestOffset * count; + } + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BlockSet.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BlockSet.java index ac7747c..3f13564 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BlockSet.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BlockSet.java @@ -1,50 +1,50 @@ package nl.requios.effortlessbuilding.buildmodifier; -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; import java.util.List; public class BlockSet { - private List coordinates; - private List previousBlockStates; - private List newBlockStates; - private Vec3d hitVec; - private BlockPos firstPos; - private BlockPos secondPos; + private final List coordinates; + private final List previousBlockStates; + private final List newBlockStates; + private final Vec3 hitVec; + private final BlockPos firstPos; + private final BlockPos secondPos; - public BlockSet(List coordinates, List previousBlockStates, List 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 BlockSet(List coordinates, List previousBlockStates, List newBlockStates, Vec3 hitVec, + BlockPos firstPos, BlockPos secondPos) { + this.coordinates = coordinates; + this.previousBlockStates = previousBlockStates; + this.newBlockStates = newBlockStates; + this.hitVec = hitVec; + this.firstPos = firstPos; + this.secondPos = secondPos; + } - public List getCoordinates() { - return coordinates; - } + public List getCoordinates() { + return coordinates; + } - public List getPreviousBlockStates() { - return previousBlockStates; - } + public List getPreviousBlockStates() { + return previousBlockStates; + } - public List getNewBlockStates() { - return newBlockStates; - } + public List getNewBlockStates() { + return newBlockStates; + } - public Vec3d getHitVec() { - return hitVec; - } + public Vec3 getHitVec() { + return hitVec; + } - public BlockPos getFirstPos() { - return firstPos; - } + public BlockPos getFirstPos() { + return firstPos; + } - public BlockPos getSecondPos() { - return secondPos; - } + public BlockPos getSecondPos() { + return secondPos; + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java index 1fcdcdb..db2363a 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java @@ -1,259 +1,267 @@ package nl.requios.effortlessbuilding.buildmodifier; -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.Blocks; -import net.minecraft.item.ItemBlock; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.World; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.core.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.level.Level; import nl.requios.effortlessbuilding.compatibility.CompatHelper; import nl.requios.effortlessbuilding.helper.InventoryHelper; import nl.requios.effortlessbuilding.helper.SurvivalHelper; -import nl.requios.effortlessbuilding.item.ItemRandomizerBag; +import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem; import nl.requios.effortlessbuilding.render.BlockPreviewRenderer; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; public class BuildModifiers { - //Called from BuildModes - public static void onBlockPlaced(EntityPlayer player, List startCoordinates, EnumFacing sideHit, Vec3d hitVec, boolean placeStartPos) { - World world = player.world; - ItemRandomizerBag.renewRandomness(); + //Called from BuildModes + public static void onBlockPlaced(Player player, List startCoordinates, Direction sideHit, Vec3 hitVec, boolean placeStartPos) { + Level world = player.level; + AbstractRandomizerBagItem.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))); + //Format hitvec to 0.x + hitVec = new Vec3(Math.abs(hitVec.x - ((int) hitVec.x)), Math.abs(hitVec.y - ((int) hitVec.y)), Math.abs(hitVec.z - ((int) hitVec.z))); - //find coordinates and blockstates - List coordinates = findCoordinates(player, startCoordinates); - List itemStacks = new ArrayList<>(); - List blockStates = findBlockStates(player, startCoordinates, hitVec, sideHit, itemStacks); + //find coordinates and blockstates + List coordinates = findCoordinates(player, startCoordinates); + List itemStacks = new ArrayList<>(); + List blockStates = findBlockStates(player, startCoordinates, hitVec, sideHit, itemStacks); - //check if valid blockstates - if (blockStates.size() == 0 || coordinates.size() != blockStates.size()) return; + //check if valid blockstates + if (blockStates.size() == 0 || coordinates.size() != blockStates.size()) return; - //remember previous blockstates for undo - List previousBlockStates = new ArrayList<>(coordinates.size()); - List newBlockStates = new ArrayList<>(coordinates.size()); - for (BlockPos coordinate : coordinates) { - previousBlockStates.add(world.getBlockState(coordinate)); - } + //remember previous blockstates for undo + List previousBlockStates = new ArrayList<>(coordinates.size()); + List newBlockStates = new ArrayList<>(coordinates.size()); + for (BlockPos coordinate : coordinates) { + previousBlockStates.add(world.getBlockState(coordinate)); + } - if (world.isRemote) { + if (world.isClientSide) { - BlockPreviewRenderer.onBlocksPlaced(); + BlockPreviewRenderer.onBlocksPlaced(); - newBlockStates = blockStates; + newBlockStates = blockStates; - } else { + } else { - //place blocks - for (int i = placeStartPos ? 0 : 1; i < coordinates.size(); i++) { - BlockPos blockPos = coordinates.get(i); - IBlockState blockState = blockStates.get(i); - ItemStack itemStack = itemStacks.get(i); + //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.isBlockLoaded(blockPos, true)) { - //check itemstack empty - if (itemStack.isEmpty()) { - //try to find new stack, otherwise continue - itemStack = InventoryHelper.findItemStackInInventory(player, blockState.getBlock()); - if (itemStack.isEmpty()) continue; - } - SurvivalHelper.placeBlock(world, player, blockPos, blockState, itemStack, EnumFacing.UP, hitVec, false, false, false); - } - } + if (world.isLoaded(blockPos)) { + //check itemstack empty + if (itemStack.isEmpty()) { + //try to find new stack, otherwise continue + itemStack = InventoryHelper.findItemStackInInventory(player, blockState.getBlock()); + if (itemStack.isEmpty()) continue; + } + SurvivalHelper.placeBlock(world, player, blockPos, blockState, itemStack, Direction.UP, hitVec, false, false, false); + } + } - //find actual new blockstates for undo - for (BlockPos coordinate : coordinates) { - newBlockStates.add(world.getBlockState(coordinate)); - } - } + //find actual new blockstates for undo + for (BlockPos coordinate : coordinates) { + newBlockStates.add(world.getBlockState(coordinate)); + } + } - //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()); + //Set first previousBlockState to empty if in NORMAL mode, to make undo/redo work + //(Block is placed by the time it gets here, and unplaced after this) + if (!placeStartPos) previousBlockStates.set(0, Blocks.AIR.defaultBlockState()); - //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)); - } + //If all new blockstates are air then no use in adding it, no block was actually placed + //Can happen when e.g. placing one block in yourself + if (Collections.frequency(newBlockStates, Blocks.AIR.defaultBlockState()) != newBlockStates.size()) { + //add to undo stack + BlockPos firstPos = startCoordinates.get(0); + BlockPos secondPos = startCoordinates.get(startCoordinates.size() - 1); + UndoRedo.addUndo(player, new BlockSet(coordinates, previousBlockStates, newBlockStates, hitVec, firstPos, secondPos)); + } + } - } + public static void onBlockBroken(Player player, List startCoordinates, boolean breakStartPos) { + Level world = player.level; - public static void onBlockBroken(EntityPlayer player, List startCoordinates, boolean breakStartPos) { - World world = player.world; + List coordinates = findCoordinates(player, startCoordinates); - List coordinates = findCoordinates(player, startCoordinates); + if (coordinates.isEmpty()) return; - if (coordinates.isEmpty()) return; + //remember previous blockstates for undo + List previousBlockStates = new ArrayList<>(coordinates.size()); + List newBlockStates = new ArrayList<>(coordinates.size()); + for (BlockPos coordinate : coordinates) { + previousBlockStates.add(world.getBlockState(coordinate)); + } - //remember previous blockstates for undo - List previousBlockStates = new ArrayList<>(coordinates.size()); - List newBlockStates = new ArrayList<>(coordinates.size()); - for (BlockPos coordinate : coordinates) { - previousBlockStates.add(world.getBlockState(coordinate)); - } + if (world.isClientSide) { + BlockPreviewRenderer.onBlocksBroken(); - if (world.isRemote) { - BlockPreviewRenderer.onBlocksBroken(); + //list of air blockstates + for (int i = 0; i < coordinates.size(); i++) { + newBlockStates.add(Blocks.AIR.defaultBlockState()); + } - //list of air blockstates - for (BlockPos coordinate : coordinates) { - newBlockStates.add(Blocks.AIR.getDefaultState()); - } + } else { - } else { + //If the player is going to instabreak grass or a plant, only break other instabreaking things + boolean onlyInstaBreaking = !player.isCreative() && + world.getBlockState(startCoordinates.get(0)).getDestroySpeed(world, startCoordinates.get(0)) == 0f; - //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.isLoaded(coordinate) && !world.isEmptyBlock(coordinate)) { + if (!onlyInstaBreaking || world.getBlockState(coordinate).getDestroySpeed(world, coordinate) == 0f) { + SurvivalHelper.breakBlock(world, player, coordinate, false); + } + } + } - //break all those blocks - for (int i = breakStartPos ? 0 : 1; i < coordinates.size(); i++) { - BlockPos coordinate = coordinates.get(i); - if (world.isBlockLoaded(coordinate, false)) { - 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)); + } + } - //find actual new blockstates for undo - for (BlockPos coordinate : coordinates) { - newBlockStates.add(world.getBlockState(coordinate)); - } - } + //Set first newBlockState to empty if in NORMAL mode, to make undo/redo work + //(Block isn't broken yet by the time it gets here, and broken after this) + if (!breakStartPos) newBlockStates.set(0, Blocks.AIR.defaultBlockState()); - //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); + Vec3 hitVec = new Vec3(0.5, 0.5, 0.5); + UndoRedo.addUndo(player, new BlockSet(coordinates, previousBlockStates, newBlockStates, hitVec, firstPos, secondPos)); - //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 findCoordinates(Player player, List posList) { + List coordinates = new ArrayList<>(); + //Add current blocks being placed too + coordinates.addAll(posList); - public static List findCoordinates(EntityPlayer player, List posList) { - List 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 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)); + } + } - //Find mirror/array/radial mirror coordinates for each blockpos - for (BlockPos blockPos : posList) { - List 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; + } - return coordinates; - } + public static List findCoordinates(Player player, BlockPos blockPos) { + return findCoordinates(player, new ArrayList<>(Collections.singletonList(blockPos))); + } - public static List findCoordinates(EntityPlayer player, BlockPos blockPos) { - return findCoordinates(player, new ArrayList<>(Arrays.asList(blockPos))); - } + public static List findBlockStates(Player player, List posList, Vec3 hitVec, Direction facing, List itemStacks) { + List blockStates = new ArrayList<>(); + itemStacks.clear(); - public static List findBlockStates(EntityPlayer player, List posList, Vec3d hitVec, EnumFacing facing, List itemStacks) { - List blockStates = new ArrayList<>(); - itemStacks.clear(); + //Get itemstack + ItemStack itemStack = player.getItemInHand(InteractionHand.MAIN_HAND); + if (itemStack.isEmpty() || !CompatHelper.isItemBlockProxy(itemStack)) { + itemStack = player.getItemInHand(InteractionHand.OFF_HAND); + } + if (itemStack.isEmpty() || !CompatHelper.isItemBlockProxy(itemStack)) { + return blockStates; + } - //Get itemstack - ItemStack itemStack = player.getHeldItem(EnumHand.MAIN_HAND); - if (itemStack.isEmpty() || !CompatHelper.isItemBlockProxy(itemStack)) { - itemStack = player.getHeldItem(EnumHand.OFF_HAND); - } - if (itemStack.isEmpty() || !CompatHelper.isItemBlockProxy(itemStack)) { - return blockStates; - } + //Get ItemBlock stack + ItemStack itemBlock = ItemStack.EMPTY; + if (itemStack.getItem() instanceof BlockItem) itemBlock = itemStack; + else itemBlock = CompatHelper.getItemBlockFromStack(itemStack); + AbstractRandomizerBagItem.resetRandomness(); - //Get ItemBlock stack - ItemStack itemBlock = ItemStack.EMPTY; - if (itemStack.getItem() instanceof ItemBlock) 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, InteractionHand.MAIN_HAND); + if (blockState == null) continue; - //Add blocks in posList first - for (BlockPos blockPos : posList) { - if (!(itemStack.getItem() instanceof ItemBlock)) itemBlock = CompatHelper.getItemBlockFromStack(itemStack); - IBlockState blockState = getBlockStateFromItem(itemBlock, player, blockPos, facing, hitVec, EnumHand.MAIN_HAND); - blockStates.add(blockState); - itemStacks.add(itemBlock); - } + blockStates.add(blockState); + itemStacks.add(itemBlock); + } - for (BlockPos blockPos : posList) { - IBlockState blockState = getBlockStateFromItem(itemBlock, player, blockPos, facing, hitVec, EnumHand.MAIN_HAND); + for (BlockPos blockPos : posList) { + BlockState blockState = getBlockStateFromItem(itemBlock, player, blockPos, facing, hitVec, InteractionHand.MAIN_HAND); + if (blockState == null) continue; - List 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 arrayCoordinates = Array.findCoordinates(player, blockPos); - for (int i = 0; i < arrayCoordinates.size(); i++) { - BlockPos coordinate = arrayCoordinates.get(i); - IBlockState blockState1 = arrayBlockStates.get(i); - blockStates.addAll(Mirror.findBlockStates(player, coordinate, blockState1, itemStack, itemStacks)); - blockStates.addAll(RadialMirror.findBlockStates(player, coordinate, blockState1, itemStack, itemStacks)); - } + List 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 arrayCoordinates = Array.findCoordinates(player, blockPos); + for (int i = 0; i < arrayCoordinates.size(); i++) { + BlockPos coordinate = arrayCoordinates.get(i); + BlockState blockState1 = arrayBlockStates.get(i); + if (blockState1 == null) continue; - //Adjust blockstates for torches and ladders etc to place on a valid side - //TODO optimize findCoordinates (done twice now) - //TODO fix mirror + 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 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; - } + 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 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 IBlockState getBlockStateFromItem(ItemStack itemStack, EntityPlayer player, BlockPos blockPos, EnumFacing facing, Vec3d hitVec, EnumHand hand) { - return Block.getBlockFromItem(itemStack.getItem()).getStateForPlacement(player.world, blockPos, facing, - ((float) hitVec.x), ((float) hitVec.y), ((float) hitVec.z), itemStack.getMetadata(), player, hand); - } + public static BlockState getBlockStateFromItem(ItemStack itemStack, Player player, BlockPos blockPos, Direction facing, Vec3 hitVec, InteractionHand hand) { + return Block.byItem(itemStack.getItem()).getStateForPlacement(new BlockPlaceContext(new UseOnContext(player, hand, new BlockHitResult(hitVec, facing, blockPos, false)))); + } - //Returns true if equal (or both null) - public static boolean compareCoordinates(List coordinates1, List coordinates2) { - if (coordinates1 == null && coordinates2 == null) return true; - if (coordinates1 == null || coordinates2 == null) return false; + //Returns true if equal (or both null) + public static boolean compareCoordinates(List coordinates1, List 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; - } + //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); - } + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/Mirror.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/Mirror.java index 34a9b21..1e358b1 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/Mirror.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/Mirror.java @@ -1,225 +1,234 @@ package nl.requios.effortlessbuilding.buildmodifier; -import net.minecraft.block.BlockDirectional; -import net.minecraft.block.BlockDispenser; -import net.minecraft.block.BlockSlab; -import net.minecraft.block.BlockStairs; -import net.minecraft.block.state.IBlockState; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.properties.Half; +import net.minecraft.world.level.block.state.properties.SlabType; +import net.minecraft.core.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; import net.minecraftforge.items.IItemHandler; -import nl.requios.effortlessbuilding.item.ItemRandomizerBag; +import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem; import java.util.ArrayList; import java.util.List; +import net.minecraft.world.level.block.DirectionalBlock; +import net.minecraft.world.level.block.DispenserBlock; +import net.minecraft.world.level.block.SlabBlock; +import net.minecraft.world.level.block.StairBlock; +import net.minecraft.world.level.block.state.BlockState; + public class Mirror { - public static 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, drawPlanes = true; + public static List findCoordinates(Player player, BlockPos startPos) { + List coordinates = new ArrayList<>(); - public MirrorSettings() { - } + //find mirrorsettings for the player + MirrorSettings m = ModifierSettingsManager.getModifierSettings(player).getMirrorSettings(); + if (!isEnabled(m, startPos)) return coordinates; - 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; - } + if (m.mirrorX) coordinateMirrorX(m, startPos, coordinates); + if (m.mirrorY) coordinateMirrorY(m, startPos, coordinates); + if (m.mirrorZ) coordinateMirrorZ(m, startPos, coordinates); - public int getReach() { - return radius * 2; //Change ModifierSettings#setReachUpgrade too - } - } - - public static List findCoordinates(EntityPlayer player, BlockPos startPos) { - List coordinates = new ArrayList<>(); + return coordinates; + } - //find mirrorsettings for the player - MirrorSettings m = ModifierSettingsManager.getModifierSettings(player).getMirrorSettings(); - if (!isEnabled(m, startPos)) return coordinates; + private static void coordinateMirrorX(MirrorSettings m, BlockPos oldBlockPos, List 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.mirrorX) coordinateMirrorX(m, startPos, coordinates); - if (m.mirrorY) coordinateMirrorY(m, startPos, coordinates); - if (m.mirrorZ) coordinateMirrorZ(m, startPos, coordinates); + if (m.mirrorY) coordinateMirrorY(m, newBlockPos, coordinates); + if (m.mirrorZ) coordinateMirrorZ(m, newBlockPos, coordinates); + } - return coordinates; - } + private static void coordinateMirrorY(MirrorSettings m, BlockPos oldBlockPos, List 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); - private static void coordinateMirrorX(MirrorSettings m, BlockPos oldBlockPos, List 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.mirrorZ) coordinateMirrorZ(m, newBlockPos, coordinates); + } - if (m.mirrorY) coordinateMirrorY(m, newBlockPos, coordinates); - if (m.mirrorZ) coordinateMirrorZ(m, newBlockPos, coordinates); - } + private static void coordinateMirrorZ(MirrorSettings m, BlockPos oldBlockPos, List 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); + } - private static void coordinateMirrorY(MirrorSettings m, BlockPos oldBlockPos, List 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); + public static List findBlockStates(Player player, BlockPos startPos, BlockState blockState, ItemStack itemStack, List itemStacks) { + List blockStates = new ArrayList<>(); - if (m.mirrorZ) coordinateMirrorZ(m, newBlockPos, coordinates); - } + //find mirrorsettings for the player + MirrorSettings m = ModifierSettingsManager.getModifierSettings(player).getMirrorSettings(); + if (!isEnabled(m, startPos)) return blockStates; - private static void coordinateMirrorZ(MirrorSettings m, BlockPos oldBlockPos, List 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); - } + //Randomizer bag synergy + AbstractRandomizerBagItem randomizerBagItem = null; + IItemHandler bagInventory = null; + if (!itemStack.isEmpty() && itemStack.getItem() instanceof AbstractRandomizerBagItem) { + randomizerBagItem = (AbstractRandomizerBagItem) itemStack.getItem() ; + bagInventory = randomizerBagItem.getBagInventory(itemStack); + } - public static List findBlockStates(EntityPlayer player, BlockPos startPos, IBlockState blockState, ItemStack itemStack, List itemStacks) { - List blockStates = new ArrayList<>(); + if (m.mirrorX) + blockStateMirrorX(player, m, startPos, blockState, bagInventory, itemStack, InteractionHand.MAIN_HAND, blockStates, itemStacks); + if (m.mirrorY) + blockStateMirrorY(player, m, startPos, blockState, bagInventory, itemStack, InteractionHand.MAIN_HAND, blockStates, itemStacks); + if (m.mirrorZ) + blockStateMirrorZ(player, m, startPos, blockState, bagInventory, itemStack, InteractionHand.MAIN_HAND, blockStates, itemStacks); - //find mirrorsettings for the player - MirrorSettings m = ModifierSettingsManager.getModifierSettings(player).getMirrorSettings(); - if (!isEnabled(m, startPos)) return blockStates; + return blockStates; + } - //Randomizer bag synergy - IItemHandler bagInventory = null; - if (!itemStack.isEmpty() && itemStack.getItem() instanceof ItemRandomizerBag) { - bagInventory = ItemRandomizerBag.getBagInventory(itemStack); - } + private static void blockStateMirrorX(Player player, MirrorSettings m, BlockPos oldBlockPos, BlockState oldBlockState, + IItemHandler bagInventory, ItemStack itemStack, InteractionHand hand, List blockStates, List 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()); - if (m.mirrorX) blockStateMirrorX(player, m, startPos, blockState, bagInventory, itemStack, EnumHand.MAIN_HAND, blockStates, itemStacks); - if (m.mirrorY) blockStateMirrorY(player, m, startPos, blockState, bagInventory, itemStack, EnumHand.MAIN_HAND, blockStates, itemStacks); - if (m.mirrorZ) blockStateMirrorZ(player, m, startPos, blockState, bagInventory, itemStack, EnumHand.MAIN_HAND, blockStates, itemStacks); + //Randomizer bag synergy + if (bagInventory != null) { + itemStack = ((AbstractRandomizerBagItem)itemStack.getItem()).pickRandomStack(bagInventory); + oldBlockState = BuildModifiers.getBlockStateFromItem(itemStack, player, oldBlockPos, Direction.UP, new Vec3(0, 0, 0), hand); + } - return blockStates; - } + //Find blockstate + BlockState newBlockState = oldBlockState == null ? null : oldBlockState.mirror(net.minecraft.world.level.block.Mirror.FRONT_BACK); - private static void blockStateMirrorX(EntityPlayer player, MirrorSettings m, BlockPos oldBlockPos, IBlockState oldBlockState, - IItemHandler bagInventory, ItemStack itemStack, EnumHand hand, List blockStates, List 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()); + //Store blockstate and itemstack + blockStates.add(newBlockState); + itemStacks.add(itemStack); - //Randomizer bag synergy - if (bagInventory != null) { - itemStack = ItemRandomizerBag.pickRandomStack(bagInventory); - oldBlockState = BuildModifiers.getBlockStateFromItem(itemStack, player, oldBlockPos, EnumFacing.UP, new Vec3d(0, 0, 0), hand); - } + 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); + } - //Find blockstate - IBlockState newBlockState = oldBlockState == null ? null : oldBlockState.withMirror(net.minecraft.util.Mirror.FRONT_BACK); + private static void blockStateMirrorY(Player player, MirrorSettings m, BlockPos oldBlockPos, BlockState oldBlockState, + IItemHandler bagInventory, ItemStack itemStack, InteractionHand hand, List blockStates, List 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()); - //Store blockstate and itemstack - blockStates.add(newBlockState); - itemStacks.add(itemStack); + //Randomizer bag synergy + if (bagInventory != null) { + itemStack = ((AbstractRandomizerBagItem)itemStack.getItem()).pickRandomStack(bagInventory); + oldBlockState = BuildModifiers.getBlockStateFromItem(itemStack, player, oldBlockPos, Direction.UP, new Vec3(0, 0, 0), hand); + } - if (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); - } + //Find blockstate + BlockState newBlockState = oldBlockState == null ? null : getVerticalMirror(oldBlockState); - private static void blockStateMirrorY(EntityPlayer player, MirrorSettings m, BlockPos oldBlockPos, IBlockState oldBlockState, - IItemHandler bagInventory, ItemStack itemStack, EnumHand hand, List blockStates, List 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()); + //Store blockstate and itemstack + blockStates.add(newBlockState); + itemStacks.add(itemStack); - //Randomizer bag synergy - if (bagInventory != null) { - itemStack = ItemRandomizerBag.pickRandomStack(bagInventory); - oldBlockState = BuildModifiers.getBlockStateFromItem(itemStack, player, oldBlockPos, EnumFacing.UP, new Vec3d(0, 0, 0), hand); - } + if (m.mirrorZ) + blockStateMirrorZ(player, m, newBlockPos, newBlockState, bagInventory, itemStack, hand, blockStates, itemStacks); + } - //Find blockstate - IBlockState newBlockState = oldBlockState == null ? null : getVerticalMirror(oldBlockState); + private static void blockStateMirrorZ(Player player, MirrorSettings m, BlockPos oldBlockPos, BlockState oldBlockState, + IItemHandler bagInventory, ItemStack itemStack, InteractionHand hand, List blockStates, List 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); - //Store blockstate and itemstack - blockStates.add(newBlockState); - itemStacks.add(itemStack); + //Randomizer bag synergy + if (bagInventory != null) { + itemStack = ((AbstractRandomizerBagItem)itemStack.getItem()).pickRandomStack(bagInventory); + oldBlockState = BuildModifiers.getBlockStateFromItem(itemStack, player, oldBlockPos, Direction.UP, new Vec3(0, 0, 0), hand); + } - if (m.mirrorZ) blockStateMirrorZ(player, m, newBlockPos, newBlockState, bagInventory, itemStack, hand, blockStates, itemStacks); - } + //Find blockstate + BlockState newBlockState = oldBlockState == null ? null : oldBlockState.mirror(net.minecraft.world.level.block.Mirror.LEFT_RIGHT); - private static void blockStateMirrorZ(EntityPlayer player, MirrorSettings m, BlockPos oldBlockPos, IBlockState oldBlockState, - IItemHandler bagInventory, ItemStack itemStack, EnumHand hand, List blockStates, List 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); + //Store blockstate and itemstack + blockStates.add(newBlockState); + itemStacks.add(itemStack); + } - //Randomizer bag synergy - if (bagInventory != null) { - itemStack = ItemRandomizerBag.pickRandomStack(bagInventory); - oldBlockState = BuildModifiers.getBlockStateFromItem(itemStack, player, oldBlockPos, EnumFacing.UP, new Vec3d(0, 0, 0), hand); - } + public static boolean isEnabled(MirrorSettings m, BlockPos startPos) { + if (m == null || !m.enabled || (!m.mirrorX && !m.mirrorY && !m.mirrorZ)) return false; - //Find blockstate - IBlockState newBlockState = oldBlockState == null ? null : oldBlockState.withMirror(net.minecraft.util.Mirror.LEFT_RIGHT); + //within mirror distance + return !(startPos.getX() + 0.5 < m.position.x - m.radius) && !(startPos.getX() + 0.5 > m.position.x + m.radius) && + !(startPos.getY() + 0.5 < m.position.y - m.radius) && !(startPos.getY() + 0.5 > m.position.y + m.radius) && + !(startPos.getZ() + 0.5 < m.position.z - m.radius) && !(startPos.getZ() + 0.5 > m.position.z + m.radius); + } - //Store blockstate and itemstack - blockStates.add(newBlockState); - itemStacks.add(itemStack); - } + private static BlockState getVerticalMirror(BlockState blockState) { + //Stairs + if (blockState.getBlock() instanceof StairBlock) { + if (blockState.getValue(StairBlock.HALF) == Half.BOTTOM) { + return blockState.setValue(StairBlock.HALF, Half.TOP); + } else { + return blockState.setValue(StairBlock.HALF, Half.BOTTOM); + } + } - public static boolean isEnabled(MirrorSettings m, BlockPos startPos) { - if (m == null || !m.enabled || (!m.mirrorX && !m.mirrorY && !m.mirrorZ)) return false; + //Slabs + if (blockState.getBlock() instanceof SlabBlock) { + if (blockState.getValue(SlabBlock.TYPE) == SlabType.DOUBLE) { + return blockState; + } else if (blockState.getValue(SlabBlock.TYPE) == SlabType.BOTTOM) { + return blockState.setValue(SlabBlock.TYPE, SlabType.TOP); + } else { + return blockState.setValue(SlabBlock.TYPE, SlabType.BOTTOM); + } + } - //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; + //Buttons, endrod, observer, piston + if (blockState.getBlock() instanceof DirectionalBlock) { + if (blockState.getValue(DirectionalBlock.FACING) == Direction.DOWN) { + return blockState.setValue(DirectionalBlock.FACING, Direction.UP); + } else if (blockState.getValue(DirectionalBlock.FACING) == Direction.UP) { + return blockState.setValue(DirectionalBlock.FACING, Direction.DOWN); + } + } - return true; - } + //Dispenser, dropper + if (blockState.getBlock() instanceof DispenserBlock) { + if (blockState.getValue(DispenserBlock.FACING) == Direction.DOWN) { + return blockState.setValue(DispenserBlock.FACING, Direction.UP); + } else if (blockState.getValue(DispenserBlock.FACING) == Direction.UP) { + return blockState.setValue(DispenserBlock.FACING, Direction.DOWN); + } + } - private static IBlockState getVerticalMirror(IBlockState blockState) { - //Stairs - if (blockState.getBlock() instanceof BlockStairs) { - if (blockState.getValue(BlockStairs.HALF) == BlockStairs.EnumHalf.BOTTOM) { - return blockState.withProperty(BlockStairs.HALF, BlockStairs.EnumHalf.TOP); - } else { - return blockState.withProperty(BlockStairs.HALF, BlockStairs.EnumHalf.BOTTOM); - } - } + return blockState; + } - //Slabs - if (blockState.getBlock() instanceof BlockSlab) { - if (((BlockSlab) blockState.getBlock()).isDouble()) return blockState; - if (blockState.getValue(BlockSlab.HALF) == BlockSlab.EnumBlockHalf.BOTTOM) { - return blockState.withProperty(BlockSlab.HALF, BlockSlab.EnumBlockHalf.TOP); - } else { - return blockState.withProperty(BlockSlab.HALF, BlockSlab.EnumBlockHalf.BOTTOM); - } - } + public static class MirrorSettings { + public boolean enabled = false; + public Vec3 position = new Vec3(0.5, 64.5, 0.5); + public boolean mirrorX = true, mirrorY = false, mirrorZ = false; + public int radius = 10; + public boolean drawLines = true, drawPlanes = true; - //Buttons, endrod, observer, piston - if (blockState.getBlock() instanceof BlockDirectional) { - if (blockState.getValue(BlockDirectional.FACING) == EnumFacing.DOWN) { - return blockState.withProperty(BlockDirectional.FACING, EnumFacing.UP); - } else if (blockState.getValue(BlockDirectional.FACING) == EnumFacing.UP) { - return blockState.withProperty(BlockDirectional.FACING, EnumFacing.DOWN); - } - } + public MirrorSettings() { + } - //Dispenser, dropper - if (blockState.getBlock() instanceof BlockDispenser) { - if (blockState.getValue(BlockDispenser.FACING) == EnumFacing.DOWN) { - return blockState.withProperty(BlockDispenser.FACING, EnumFacing.UP); - } else if (blockState.getValue(BlockDispenser.FACING) == EnumFacing.UP) { - return blockState.withProperty(BlockDispenser.FACING, EnumFacing.DOWN); - } - } + public MirrorSettings(boolean mirrorEnabled, Vec3 position, boolean mirrorX, boolean mirrorY, boolean mirrorZ, int radius, boolean drawLines, boolean drawPlanes) { + this.enabled = mirrorEnabled; + this.position = position; + this.mirrorX = mirrorX; + this.mirrorY = mirrorY; + this.mirrorZ = mirrorZ; + this.radius = radius; + this.drawLines = drawLines; + this.drawPlanes = drawPlanes; + } - return blockState; - } + public int getReach() { + return radius * 2; //Change ModifierSettings#setReachUpgrade too + } + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/ModifierSettingsManager.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/ModifierSettingsManager.java index 1d7bdfc..7583398 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/ModifierSettingsManager.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/ModifierSettingsManager.java @@ -1,196 +1,215 @@ package nl.requios.effortlessbuilding.buildmodifier; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.world.entity.player.Player; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.network.PacketDistributor; import nl.requios.effortlessbuilding.BuildConfig; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.capability.ModifierCapabilityManager; import nl.requios.effortlessbuilding.helper.ReachHelper; import nl.requios.effortlessbuilding.network.ModifierSettingsMessage; +import nl.requios.effortlessbuilding.network.PacketHandler; + +import javax.annotation.Nonnull; @Mod.EventBusSubscriber public class ModifierSettingsManager { - //Retrieves the buildsettings of a player through the modifierCapability capability - //Never returns null - public static ModifierSettings getModifierSettings(EntityPlayer player){ - if (player.hasCapability(ModifierCapabilityManager.modifierCapability, null)) { - ModifierCapabilityManager.IModifierCapability capability = player.getCapability( - ModifierCapabilityManager.modifierCapability, null); - if (capability.getModifierData() == null) { - capability.setModifierData(new ModifierSettings()); - } - return capability.getModifierData(); - } - throw new IllegalArgumentException("Player does not have modifierCapability capability"); - } + //Retrieves the buildsettings of a player through the modifierCapability capability + //Never returns null + @Nonnull + public static ModifierSettings getModifierSettings(Player player) { - public static void setModifierSettings(EntityPlayer player, ModifierSettings modifierSettings) { - if (player == null) { - EffortlessBuilding.log("Cannot set buildsettings, player is null"); - return; - } - if (player.hasCapability(ModifierCapabilityManager.modifierCapability, null)) { - ModifierCapabilityManager.IModifierCapability capability = player.getCapability( - ModifierCapabilityManager.modifierCapability, null); - capability.setModifierData(modifierSettings); - } else { - EffortlessBuilding.log(player, "Saving buildsettings failed."); - } - } + LazyOptional modifierCapability = + player.getCapability(ModifierCapabilityManager.MODIFIER_CAPABILITY, null); - public static String sanitize(ModifierSettings modifierSettings, EntityPlayer player) { - int maxReach = ReachHelper.getMaxReach(player); - String error = ""; + if (modifierCapability.isPresent()) { + ModifierCapabilityManager.IModifierCapability capability = modifierCapability.orElse(null); + if (capability.getModifierData() == null){ + capability.setModifierData(new ModifierSettings()); + } + return capability.getModifierData(); + } - //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) + ". "; - } + EffortlessBuilding.logger.warn("Player does not have modifierCapability: " + player); + //Return dummy settings + return new ModifierSettings(); + } - //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."; - } + public static void setModifierSettings(Player player, ModifierSettings modifierSettings) { + if (player == null) { + EffortlessBuilding.log("Cannot set buildsettings, player is null"); + return; + } - if (a.getReach() > maxReach) { - a.count = 0; - error += "Array exceeds your maximum reach of " + maxReach + ". Array count has been reset to 0. "; - } + LazyOptional modifierCapability = + player.getCapability(ModifierCapabilityManager.MODIFIER_CAPABILITY, null); - //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."; - } + modifierCapability.ifPresent((capability) -> { + capability.setModifierData(modifierSettings); + }); - 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) + ". "; - } + if (!modifierCapability.isPresent()) { + EffortlessBuilding.log(player, "Saving buildsettings failed."); + } + } - //Other - if (modifierSettings.reachUpgrade < 0) { - modifierSettings.reachUpgrade = 0; - } - if (modifierSettings.reachUpgrade > 3) { - modifierSettings.reachUpgrade = 3; - } + public static String sanitize(ModifierSettings modifierSettings, Player player) { + int maxReach = ReachHelper.getMaxReach(player); + String error = ""; - return 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) + ". "; + } - public static class ModifierSettings { - private Mirror.MirrorSettings mirrorSettings; - private Array.ArraySettings arraySettings; - private RadialMirror.RadialMirrorSettings radialMirrorSettings; - private boolean quickReplace = false; - private int reachUpgrade = 0; + //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."; + } - public ModifierSettings() { - mirrorSettings = new Mirror.MirrorSettings(); - arraySettings = new Array.ArraySettings(); - radialMirrorSettings = new RadialMirror.RadialMirrorSettings(); - } + if (a.getReach() > maxReach) { + a.count = 0; + error += "Array exceeds your maximum reach of " + maxReach + ". Array count has been reset to 0. "; + } - 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; - } + //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."; + } - public Mirror.MirrorSettings getMirrorSettings() { - if (this.mirrorSettings == null) this.mirrorSettings = new Mirror.MirrorSettings(); - return this.mirrorSettings; - } + 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) + ". "; + } - public void setMirrorSettings(Mirror.MirrorSettings mirrorSettings) { - if (mirrorSettings == null) return; - this.mirrorSettings = mirrorSettings; - } + //Other + if (modifierSettings.reachUpgrade < 0) { + modifierSettings.reachUpgrade = 0; + } + if (modifierSettings.reachUpgrade > 3) { + modifierSettings.reachUpgrade = 3; + } - public Array.ArraySettings getArraySettings() { - if (this.arraySettings == null) this.arraySettings = new Array.ArraySettings(); - return this.arraySettings; - } + return error; + } - public void setArraySettings(Array.ArraySettings arraySettings) { - if (arraySettings == null) return; - this.arraySettings = arraySettings; - } + public static void handleNewPlayer(Player player) { + //Makes sure player has modifier settings (if it doesnt it will create it) + getModifierSettings(player); - public RadialMirror.RadialMirrorSettings getRadialMirrorSettings() { - if (this.radialMirrorSettings == null) this.radialMirrorSettings = new RadialMirror.RadialMirrorSettings(); - return this.radialMirrorSettings; - } + //Only on server + if (!player.level.isClientSide) { + //Send to client + ModifierSettingsMessage msg = new ModifierSettingsMessage(getModifierSettings(player)); + PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), msg); + } + } - public void setRadialMirrorSettings(RadialMirror.RadialMirrorSettings radialMirrorSettings) { - if (radialMirrorSettings == null) return; - this.radialMirrorSettings = radialMirrorSettings; - } + 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 boolean doQuickReplace() { - return quickReplace; - } + public ModifierSettings() { + mirrorSettings = new Mirror.MirrorSettings(); + arraySettings = new Array.ArraySettings(); + radialMirrorSettings = new RadialMirror.RadialMirrorSettings(); + } - public void setQuickReplace(boolean quickReplace) { - this.quickReplace = quickReplace; - } + 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 int getReachUpgrade() { - return reachUpgrade; - } + public Mirror.MirrorSettings getMirrorSettings() { + if (this.mirrorSettings == null) this.mirrorSettings = new Mirror.MirrorSettings(); + return this.mirrorSettings; + } - public void setReachUpgrade(int reachUpgrade) { - this.reachUpgrade = reachUpgrade; - //Set mirror radius to max - int reach = 10; - switch (reachUpgrade) { - case 0: reach = BuildConfig.reach.maxReachLevel0; break; - case 1: reach = BuildConfig.reach.maxReachLevel1; break; - case 2: reach = BuildConfig.reach.maxReachLevel2; break; - case 3: reach = BuildConfig.reach.maxReachLevel3; break; - } + public void setMirrorSettings(Mirror.MirrorSettings mirrorSettings) { + if (mirrorSettings == null) return; + this.mirrorSettings = mirrorSettings; + } - EffortlessBuilding.log("before "+this.mirrorSettings.radius); + public Array.ArraySettings getArraySettings() { + if (this.arraySettings == null) this.arraySettings = new Array.ArraySettings(); + return this.arraySettings; + } - if (this.mirrorSettings != null) - this.mirrorSettings.radius = reach / 2; - if (this.radialMirrorSettings != null) - this.radialMirrorSettings.radius = reach / 2; + public void setArraySettings(Array.ArraySettings arraySettings) { + if (arraySettings == null) return; + this.arraySettings = arraySettings; + } - EffortlessBuilding.log("after "+this.mirrorSettings.radius); - } - } + public RadialMirror.RadialMirrorSettings getRadialMirrorSettings() { + if (this.radialMirrorSettings == null) this.radialMirrorSettings = new RadialMirror.RadialMirrorSettings(); + return this.radialMirrorSettings; + } - public static void handleNewPlayer(EntityPlayer player){ - if (getModifierSettings(player) == null) { - setModifierSettings(player, new ModifierSettings()); - } + public void setRadialMirrorSettings(RadialMirror.RadialMirrorSettings radialMirrorSettings) { + if (radialMirrorSettings == null) return; + this.radialMirrorSettings = radialMirrorSettings; + } - //Only on server - if (!player.world.isRemote) { - //Send to client - ModifierSettingsMessage msg = new ModifierSettingsMessage(getModifierSettings(player)); - EffortlessBuilding.packetHandler.sendTo(msg, (EntityPlayerMP) player); - } - } + 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; + } + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/RadialMirror.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/RadialMirror.java index 32b4148..347dc2b 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/RadialMirror.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/RadialMirror.java @@ -1,195 +1,194 @@ package nl.requios.effortlessbuilding.buildmodifier; -import net.minecraft.block.state.IBlockState; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -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.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.core.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.core.BlockPos; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; import net.minecraftforge.items.IItemHandler; -import nl.requios.effortlessbuilding.item.ItemRandomizerBag; +import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem; import java.util.ArrayList; import java.util.List; 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, drawPlanes = false; + public static List findCoordinates(Player player, BlockPos startPos) { + List coordinates = new ArrayList<>(); - public RadialMirrorSettings() { - } + //find radial mirror settings for the player + RadialMirrorSettings r = ModifierSettingsManager.getModifierSettings(player).getRadialMirrorSettings(); + if (!isEnabled(r, startPos)) return coordinates; - 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; - } + //get angle between slices + double sliceAngle = 2 * Math.PI / r.slices; - public int getReach() { - return radius * 2; - } - } + Vec3 startVec = new Vec3(startPos.getX() + 0.5f, startPos.getY() + 0.5f, startPos.getZ() + 0.5f); + Vec3 relStartVec = startVec.subtract(r.position); - public static List findCoordinates(EntityPlayer player, BlockPos startPos) { - List coordinates = new ArrayList<>(); + double startAngleToCenter = Mth.atan2(relStartVec.x, relStartVec.z); + if (startAngleToCenter < 0) startAngleToCenter += Math.PI; + double startAngleInSlice = startAngleToCenter % sliceAngle; - //find radial mirror settings for the player - RadialMirrorSettings r = ModifierSettingsManager.getModifierSettings(player).getRadialMirrorSettings(); - if (!isEnabled(r, startPos)) return coordinates; + for (int i = 1; i < r.slices; i++) { + double curAngle = sliceAngle * i; - //get angle between slices - double sliceAngle = 2 * Math.PI / r.slices; + //alternate mirroring of slices + if (r.alternate && i % 2 == 1) { + curAngle = curAngle - startAngleInSlice + (sliceAngle - startAngleInSlice); + } - Vec3d startVec = new Vec3d(startPos.getX() + 0.5f, startPos.getY() + 0.5f, startPos.getZ() + 0.5f); - Vec3d relStartVec = startVec.subtract(r.position); + Vec3 relNewVec = relStartVec.yRot((float) curAngle); + BlockPos newBlockPos = new BlockPos(r.position.add(relNewVec)); + if (!coordinates.contains(newBlockPos) && !newBlockPos.equals(startPos)) coordinates.add(newBlockPos); + } - double startAngleToCenter = MathHelper.atan2(relStartVec.x, relStartVec.z); - if (startAngleToCenter < 0) startAngleToCenter += Math.PI; - double startAngleInSlice = startAngleToCenter % sliceAngle; + return coordinates; + } - for (int i = 1; i < r.slices; i++) { - double curAngle = sliceAngle * i; + public static List findBlockStates(Player player, BlockPos startPos, BlockState blockState, ItemStack itemStack, List itemStacks) { + List blockStates = new ArrayList<>(); + List coordinates = new ArrayList<>(); //to keep track of duplicates - //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)) coordinates.add(newBlockPos); - } - - return coordinates; - } - - public static List findBlockStates(EntityPlayer player, BlockPos startPos, IBlockState blockState, ItemStack itemStack, List itemStacks) { - List blockStates = new ArrayList<>(); - List 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; + //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; + //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); + Vec3 startVec = new Vec3(startPos.getX() + 0.5f, startPos.getY() + 0.5f, startPos.getZ() + 0.5f); + Vec3 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; + double startAngleToCenter = Mth.atan2(relStartVec.x, relStartVec.z); + double startAngleToCenterMod = startAngleToCenter < 0 ? startAngleToCenter + Math.PI : startAngleToCenter; + double startAngleInSlice = startAngleToCenterMod % sliceAngle; - //Rotate the original blockstate - blockState = rotateOriginalBlockState(startAngleToCenter, blockState); + //Rotate the original blockstate + blockState = rotateOriginalBlockState(player, startPos, startAngleToCenter, blockState); - //Randomizer bag synergy - IItemHandler bagInventory = null; - if (!itemStack.isEmpty() && itemStack.getItem() instanceof ItemRandomizerBag) { - bagInventory = ItemRandomizerBag.getBagInventory(itemStack); - } + //Randomizer bag synergy + AbstractRandomizerBagItem randomizerBagItem = null; + IItemHandler bagInventory = null; + if (!itemStack.isEmpty() && itemStack.getItem() instanceof AbstractRandomizerBagItem) { + randomizerBagItem = (AbstractRandomizerBagItem) itemStack.getItem() ; + bagInventory = randomizerBagItem.getBagInventory(itemStack); + } - IBlockState newBlockState; - for (int i = 1; i < r.slices; i++) { - newBlockState = blockState; - double curAngle = sliceAngle * i; + 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); - } + //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); + Vec3 relNewVec = relStartVec.yRot((float) curAngle); + BlockPos newBlockPos = new BlockPos(r.position.add(relNewVec)); + if (coordinates.contains(newBlockPos) || newBlockPos.equals(startPos)) continue; //filter out duplicates + coordinates.add(newBlockPos); - //Randomizer bag synergy - if (bagInventory != null) { - itemStack = ItemRandomizerBag.pickRandomStack(bagInventory); - newBlockState = BuildModifiers - .getBlockStateFromItem(itemStack, player, startPos, EnumFacing.UP, new Vec3d(0, 0, 0), EnumHand.MAIN_HAND); + //Randomizer bag synergy + if (randomizerBagItem != null) { + itemStack = randomizerBagItem.pickRandomStack(bagInventory); + newBlockState = BuildModifiers + .getBlockStateFromItem(itemStack, player, startPos, Direction.UP, new Vec3(0, 0, 0), InteractionHand.MAIN_HAND); - newBlockState = rotateOriginalBlockState(startAngleToCenter, newBlockState); - } + newBlockState = rotateOriginalBlockState(player, startPos, startAngleToCenter, newBlockState); + } - //rotate - newBlockState = rotateBlockState(relNewVec, newBlockState, r.alternate && i%2 == 1); + //rotate + newBlockState = rotateBlockState(player, startPos, relNewVec, newBlockState, r.alternate && i % 2 == 1); - blockStates.add(newBlockState); - itemStacks.add(itemStack); - } + blockStates.add(newBlockState); + itemStacks.add(itemStack); + } - return blockStates; - } + return blockStates; + } - private static IBlockState rotateOriginalBlockState(double startAngleToCenter, IBlockState blockState) { - IBlockState newBlockState = blockState; + private static BlockState rotateOriginalBlockState(Player player, BlockPos startPos, double startAngleToCenter, BlockState blockState) { + BlockState newBlockState = blockState; - if (startAngleToCenter < -0.751 * Math.PI || startAngleToCenter > 0.749 * Math.PI) { - newBlockState = blockState.withRotation(Rotation.CLOCKWISE_180); - } else if (startAngleToCenter < -0.251 * Math.PI) { - newBlockState = blockState.withRotation(Rotation.COUNTERCLOCKWISE_90); - } else if (startAngleToCenter > 0.249 * Math.PI) { - newBlockState = blockState.withRotation(Rotation.CLOCKWISE_90); - } + if (startAngleToCenter < -0.751 * Math.PI || startAngleToCenter > 0.749 * Math.PI) { + newBlockState = blockState.rotate(player.level, startPos, Rotation.CLOCKWISE_180); + } else if (startAngleToCenter < -0.251 * Math.PI) { + newBlockState = blockState.rotate(player.level, startPos, Rotation.COUNTERCLOCKWISE_90); + } else if (startAngleToCenter > 0.249 * Math.PI) { + newBlockState = blockState.rotate(player.level, startPos, Rotation.CLOCKWISE_90); + } - return newBlockState; - } + return newBlockState; + } - private static IBlockState rotateBlockState(Vec3d relVec, IBlockState blockState, boolean alternate) { - IBlockState newBlockState; - double angleToCenter = MathHelper.atan2(relVec.x, relVec.z); //between -PI and PI + private static BlockState rotateBlockState(Player player, BlockPos startPos, Vec3 relVec, BlockState blockState, boolean alternate) { + BlockState newBlockState; + double angleToCenter = Mth.atan2(relVec.x, relVec.z); //between -PI and PI - if (angleToCenter < -0.751 * Math.PI || angleToCenter > 0.749 * Math.PI) { - newBlockState = blockState.withRotation(Rotation.CLOCKWISE_180); - if (alternate) { - newBlockState = newBlockState.withMirror(Mirror.FRONT_BACK); - } - } else if (angleToCenter < -0.251 * Math.PI) { - newBlockState = blockState.withRotation(Rotation.CLOCKWISE_90); - if (alternate) { - newBlockState = newBlockState.withMirror(Mirror.LEFT_RIGHT); - } - } else if (angleToCenter > 0.249 * Math.PI) { - newBlockState = blockState.withRotation(Rotation.COUNTERCLOCKWISE_90); - if (alternate) { - newBlockState = newBlockState.withMirror(Mirror.LEFT_RIGHT); - } - } else { - newBlockState = blockState; - if (alternate) { - newBlockState = newBlockState.withMirror(Mirror.FRONT_BACK); - } - } + if (angleToCenter < -0.751 * Math.PI || angleToCenter > 0.749 * Math.PI) { + newBlockState = blockState.rotate(player.level, startPos, Rotation.CLOCKWISE_180); + if (alternate) { + newBlockState = newBlockState.mirror(Mirror.FRONT_BACK); + } + } else if (angleToCenter < -0.251 * Math.PI) { + newBlockState = blockState.rotate(player.level, startPos, Rotation.CLOCKWISE_90); + if (alternate) { + newBlockState = newBlockState.mirror(Mirror.LEFT_RIGHT); + } + } else if (angleToCenter > 0.249 * Math.PI) { + newBlockState = blockState.rotate(player.level, startPos, Rotation.COUNTERCLOCKWISE_90); + if (alternate) { + newBlockState = newBlockState.mirror(Mirror.LEFT_RIGHT); + } + } else { + newBlockState = blockState; + if (alternate) { + newBlockState = newBlockState.mirror(Mirror.FRONT_BACK); + } + } - return newBlockState; - } + return newBlockState; + } - public static boolean isEnabled(RadialMirrorSettings r, BlockPos startPos) { - if (r == null || !r.enabled) return false; + 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 !(new Vec3(startPos.getX() + 0.5, startPos.getY() + 0.5, startPos.getZ() + 0.5).subtract(r.position).lengthSqr() > + r.radius * r.radius); + } - return true; - } + public static class RadialMirrorSettings { + public boolean enabled = false; + public Vec3 position = new Vec3(0.5, 64.5, 0.5); + public int slices = 4; + public boolean alternate = false; + public int radius = 20; + public boolean drawLines = true, drawPlanes = false; + + public RadialMirrorSettings() { + } + + public RadialMirrorSettings(boolean enabled, Vec3 position, int slices, boolean alternate, int radius, boolean drawLines, boolean drawPlanes) { + this.enabled = enabled; + this.position = position; + this.slices = slices; + this.alternate = alternate; + this.radius = radius; + this.drawLines = drawLines; + this.drawPlanes = drawPlanes; + } + + public int getReach() { + return radius * 2; + } + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java index ada64e0..8d0152c 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java @@ -1,15 +1,15 @@ package nl.requios.effortlessbuilding.buildmodifier; -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.Blocks; -import net.minecraft.item.Item; -import net.minecraft.item.ItemBlock; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.core.Direction; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; +import net.minecraft.server.level.ServerLevel; import nl.requios.effortlessbuilding.BuildConfig; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.helper.FixedStack; @@ -21,26 +21,26 @@ 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> undoStacksClient = new HashMap<>(); - private static Map> undoStacksServer = new HashMap<>(); - private static Map> redoStacksClient = new HashMap<>(); - private static Map> redoStacksServer = new HashMap<>(); + //Undo and redo stacks per player + //Gets added to twice in singleplayer (server and client) if not careful. So separate stacks. + private static final Map> undoStacksClient = new HashMap<>(); + private static final Map> undoStacksServer = new HashMap<>(); + private static final Map> redoStacksClient = new HashMap<>(); + private static final Map> redoStacksServer = new HashMap<>(); - //add to undo stack - public static void addUndo(EntityPlayer player, BlockSet blockSet) { - Map> undoStacks = player.world.isRemote ? undoStacksClient : undoStacksServer; + //add to undo stack + public static void addUndo(Player player, BlockSet blockSet) { + Map> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer; - //Assert coordinates is as long as previous and new blockstate lists - if (blockSet.getCoordinates().size() != blockSet.getPreviousBlockStates().size() || - blockSet.getCoordinates().size() != blockSet.getNewBlockStates().size()) { - EffortlessBuilding.logger.error("Coordinates and blockstate lists are not equal length. Coordinates: {}. Previous blockstates: {}. New blockstates: {}.", - blockSet.getCoordinates().size(), blockSet.getPreviousBlockStates().size(), blockSet.getNewBlockStates().size()); - } + //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 + //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: {}.", @@ -48,182 +48,192 @@ public class UndoRedo { // } // } - //If no stack exists, make one - if (!undoStacks.containsKey(player.getUniqueID())) { - undoStacks.put(player.getUniqueID(), new FixedStack<>(new BlockSet[BuildConfig.survivalBalancers.undoStackSize])); - } + //If no stack exists, make one + if (!undoStacks.containsKey(player.getUUID())) { + undoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[BuildConfig.survivalBalancers.undoStackSize.get()])); + } + + undoStacks.get(player.getUUID()).push(blockSet); + } + + private static void addRedo(Player player, BlockSet blockSet) { + Map> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer; + + //(No asserts necessary, it's private) + + //If no stack exists, make one + if (!redoStacks.containsKey(player.getUUID())) { + redoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[BuildConfig.survivalBalancers.undoStackSize.get()])); + } + + redoStacks.get(player.getUUID()).push(blockSet); + } + + public static boolean undo(Player player) { + Map> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer; + + if (!undoStacks.containsKey(player.getUUID())) return false; + + FixedStack undoStack = undoStacks.get(player.getUUID()); + + if (undoStack.isEmpty()) return false; + + BlockSet blockSet = undoStack.pop(); + List coordinates = blockSet.getCoordinates(); + List previousBlockStates = blockSet.getPreviousBlockStates(); + List newBlockStates = blockSet.getNewBlockStates(); + Vec3 hitVec = blockSet.getHitVec(); + + //Find up to date itemstacks in player inventory + List itemStacks = findItemStacksInInventory(player, previousBlockStates); + + if (player.level.isClientSide) { + BlockPreviewRenderer.onBlocksBroken(coordinates, itemStacks, newBlockStates, blockSet.getSecondPos(), blockSet.getFirstPos()); + } else { + //break all those blocks, reset to what they were + for (int i = 0; i < coordinates.size(); i++) { + BlockPos coordinate = coordinates.get(i); + ItemStack itemStack = itemStacks.get(i); + + if (previousBlockStates.get(i).equals(newBlockStates.get(i))) continue; + + //get blockstate from itemstack + BlockState previousBlockState = Blocks.AIR.defaultBlockState(); + if (itemStack.getItem() instanceof BlockItem) { + previousBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState(); + } + + if (player.level.isLoaded(coordinate)) { + //check itemstack empty + if (itemStack.isEmpty()) { + itemStack = findItemStackInInventory(player, previousBlockStates.get(i)); + //get blockstate from new itemstack + if (!itemStack.isEmpty() && itemStack.getItem() instanceof BlockItem) { + previousBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState(); + } else { + if (previousBlockStates.get(i).getBlock() != Blocks.AIR) + EffortlessBuilding.logTranslate(player, "", previousBlockStates.get(i).getBlock().getDescriptionId(), " not found in inventory", true); + previousBlockState = Blocks.AIR.defaultBlockState(); + } + } + if (itemStack.isEmpty()) SurvivalHelper.breakBlock(player.level, player, coordinate, true); + //if previousBlockState is air, placeBlock will set it to air + SurvivalHelper.placeBlock(player.level, player, coordinate, previousBlockState, itemStack, Direction.UP, hitVec, true, false, false); + } + } + } + + //add to redo + addRedo(player, blockSet); + + return true; + } + + public static boolean redo(Player player) { + Map> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer; + + if (!redoStacks.containsKey(player.getUUID())) return false; + + FixedStack redoStack = redoStacks.get(player.getUUID()); + + if (redoStack.isEmpty()) return false; + + BlockSet blockSet = redoStack.pop(); + List coordinates = blockSet.getCoordinates(); + List previousBlockStates = blockSet.getPreviousBlockStates(); + List newBlockStates = blockSet.getNewBlockStates(); + Vec3 hitVec = blockSet.getHitVec(); + + //Find up to date itemstacks in player inventory + List itemStacks = findItemStacksInInventory(player, newBlockStates); + + if (player.level.isClientSide) { + BlockPreviewRenderer.onBlocksPlaced(coordinates, itemStacks, newBlockStates, blockSet.getFirstPos(), blockSet.getSecondPos()); + } else { + //place blocks + for (int i = 0; i < coordinates.size(); i++) { + BlockPos coordinate = coordinates.get(i); + ItemStack itemStack = itemStacks.get(i); + + if (previousBlockStates.get(i).equals(newBlockStates.get(i))) continue; + + //get blockstate from itemstack + BlockState newBlockState = Blocks.AIR.defaultBlockState(); + if (itemStack.getItem() instanceof BlockItem) { + newBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState(); + } + + if (player.level.isLoaded(coordinate)) { + //check itemstack empty + if (itemStack.isEmpty()) { + itemStack = findItemStackInInventory(player, newBlockStates.get(i)); + //get blockstate from new itemstack + if (!itemStack.isEmpty() && itemStack.getItem() instanceof BlockItem) { + newBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState(); + } else { + if (newBlockStates.get(i).getBlock() != Blocks.AIR) + EffortlessBuilding.logTranslate(player, "", newBlockStates.get(i).getBlock().getDescriptionId(), " not found in inventory", true); + newBlockState = Blocks.AIR.defaultBlockState(); + } + } + if (itemStack.isEmpty()) SurvivalHelper.breakBlock(player.level, player, coordinate, true); + SurvivalHelper.placeBlock(player.level, player, coordinate, newBlockState, itemStack, Direction.UP, hitVec, true, false, false); + } + } + } + + //add to undo + addUndo(player, blockSet); + + return true; + } + + public static void clear(Player player) { + Map> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer; + Map> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer; + if (undoStacks.containsKey(player.getUUID())) { + undoStacks.get(player.getUUID()).clear(); + } + if (redoStacks.containsKey(player.getUUID())) { + redoStacks.get(player.getUUID()).clear(); + } + } + + private static List findItemStacksInInventory(Player player, List blockStates) { + List itemStacks = new ArrayList<>(blockStates.size()); + for (BlockState blockState : blockStates) { + itemStacks.add(findItemStackInInventory(player, blockState)); + } + return itemStacks; + } + + private static ItemStack findItemStackInInventory(Player player, BlockState blockState) { + ItemStack itemStack = ItemStack.EMPTY; + if (blockState == null) return itemStack; + + //First try previousBlockStates + //TODO try to find itemstack with right blockstate first + // then change line 103 back (get state from item) + itemStack = InventoryHelper.findItemStackInInventory(player, blockState.getBlock()); - undoStacks.get(player.getUniqueID()).push(blockSet); - } + //then anything it drops + if (itemStack.isEmpty()) { + //Cannot check drops on clientside because loot tables are server only + if (!player.level.isClientSide) { + List itemsDropped = Block.getDrops(blockState, (ServerLevel) player.level, BlockPos.ZERO, null); + for (ItemStack itemStackDropped : itemsDropped) { + if (itemStackDropped.getItem() instanceof BlockItem) { + Block block = ((BlockItem) itemStackDropped.getItem()).getBlock(); + itemStack = InventoryHelper.findItemStackInInventory(player, block); + } + } + } + } - private static void addRedo(EntityPlayer player, BlockSet blockSet) { - Map> redoStacks = player.world.isRemote ? redoStacksClient : redoStacksServer; + //then air + //(already empty) - //(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])); - } - - redoStacks.get(player.getUniqueID()).push(blockSet); - } - - public static boolean undo(EntityPlayer player) { - Map> undoStacks = player.world.isRemote ? undoStacksClient : undoStacksServer; - - if (!undoStacks.containsKey(player.getUniqueID())) return false; - - FixedStack undoStack = undoStacks.get(player.getUniqueID()); - - if (undoStack.isEmpty()) return false; - - BlockSet blockSet = undoStack.pop(); - List coordinates = blockSet.getCoordinates(); - List previousBlockStates = blockSet.getPreviousBlockStates(); - List newBlockStates = blockSet.getNewBlockStates(); - Vec3d hitVec = blockSet.getHitVec(); - - //Find up to date itemstacks in player inventory - List 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 - IBlockState previousBlockState = Blocks.AIR.getDefaultState(); - if (itemStack.getItem() instanceof ItemBlock) { - previousBlockState = previousBlockStates.get(i);//((ItemBlock) itemStack.getItem()).getBlock().getDefaultState(); - } - - if (player.world.isBlockLoaded(coordinate, true)) { - //check itemstack empty - if (itemStack.isEmpty()) { - itemStack = findItemStackInInventory(player, previousBlockStates.get(i)); - //get blockstate from new itemstack - if (!itemStack.isEmpty() && itemStack.getItem() instanceof ItemBlock) { - previousBlockState = previousBlockStates.get(i);//((ItemBlock) itemStack.getItem()).getBlock().getDefaultState(); - } else { - 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, EnumFacing.UP, hitVec, true, false, false); - } - } - } - - //add to redo - addRedo(player, blockSet); - - return true; - } - - public static boolean redo(EntityPlayer player) { - Map> redoStacks = player.world.isRemote ? redoStacksClient : redoStacksServer; - - if (!redoStacks.containsKey(player.getUniqueID())) return false; - - FixedStack redoStack = redoStacks.get(player.getUniqueID()); - - if (redoStack.isEmpty()) return false; - - BlockSet blockSet = redoStack.pop(); - List coordinates = blockSet.getCoordinates(); - List previousBlockStates = blockSet.getPreviousBlockStates(); - List newBlockStates = blockSet.getNewBlockStates(); - Vec3d hitVec = blockSet.getHitVec(); - - //Find up to date itemstacks in player inventory - List 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 - IBlockState newBlockState = Blocks.AIR.getDefaultState(); - if (itemStack.getItem() instanceof ItemBlock) { - newBlockState = newBlockStates.get(i);//((ItemBlock) itemStack.getItem()).getBlock().getDefaultState(); - } - - if (player.world.isBlockLoaded(coordinate, true)) { - //check itemstack empty - if (itemStack.isEmpty()) { - itemStack = findItemStackInInventory(player, newBlockStates.get(i)); - //get blockstate from new itemstack - if (!itemStack.isEmpty() && itemStack.getItem() instanceof ItemBlock) { - newBlockState = newBlockStates.get(i);//((ItemBlock) itemStack.getItem()).getBlock().getDefaultState(); - } else { - newBlockState = Blocks.AIR.getDefaultState(); - } - } - if (itemStack.isEmpty()) SurvivalHelper.breakBlock(player.world, player, coordinate, true); - SurvivalHelper.placeBlock(player.world, player, coordinate, newBlockState, itemStack, EnumFacing.UP, hitVec, true, false, false); - } - } - } - - //add to undo - addUndo(player, blockSet); - - return true; - } - - public static void clear(EntityPlayer player) { - Map> undoStacks = player.world.isRemote ? undoStacksClient : undoStacksServer; - Map> 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 findItemStacksInInventory(EntityPlayer player, List blockStates) { - List itemStacks = new ArrayList<>(blockStates.size()); - for (IBlockState blockState : blockStates) { - itemStacks.add(findItemStackInInventory(player, blockState)); - } - return itemStacks; - } - - private static ItemStack findItemStackInInventory(EntityPlayer player, IBlockState blockState) { - ItemStack itemStack = ItemStack.EMPTY; - - //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()) { - Item itemDropped = blockState.getBlock().getItemDropped(blockState, player.world.rand, 10); - if (itemDropped instanceof ItemBlock) { - Block block = ((ItemBlock) itemDropped).getBlock(); - itemStack = InventoryHelper.findItemStackInInventory(player, block); - } - } - - //then air - //(already empty) - - return itemStack; - } + return itemStack; + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/capability/ItemHandlerCapabilityProvider.java b/src/main/java/nl/requios/effortlessbuilding/capability/ItemHandlerCapabilityProvider.java index 238eb3b..9c44516 100644 --- a/src/main/java/nl/requios/effortlessbuilding/capability/ItemHandlerCapabilityProvider.java +++ b/src/main/java/nl/requios/effortlessbuilding/capability/ItemHandlerCapabilityProvider.java @@ -1,42 +1,37 @@ package nl.requios.effortlessbuilding.capability; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumFacing; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.core.Direction; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ICapabilitySerializable; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemStackHandler; -import nl.requios.effortlessbuilding.item.ItemRandomizerBag; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public class ItemHandlerCapabilityProvider implements ICapabilitySerializable { - IItemHandler itemHandler = new ItemStackHandler(ItemRandomizerBag.INV_SIZE); +public class ItemHandlerCapabilityProvider implements ICapabilitySerializable { + IItemHandler itemHandler; - @Override - public boolean hasCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { - if (capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) - return true; - return false; - } + public ItemHandlerCapabilityProvider(int size) { + itemHandler = new ItemStackHandler(size); + } - @Nullable - @Override - public T getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { - if (capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) - return (T) itemHandler; - return null; - } + @Nonnull + @Override + public LazyOptional getCapability(@Nonnull Capability cap, @Nullable Direction side) { + return CapabilityItemHandler.ITEM_HANDLER_CAPABILITY.orEmpty(cap, LazyOptional.of(() -> itemHandler)); + } - @Override - public NBTTagCompound serializeNBT() { - return ((ItemStackHandler) itemHandler).serializeNBT(); - } + @Override + public CompoundTag serializeNBT() { + return ((ItemStackHandler) itemHandler).serializeNBT(); + } - @Override - public void deserializeNBT(NBTTagCompound nbt) { - ((ItemStackHandler) itemHandler).deserializeNBT(nbt); - } + @Override + public void deserializeNBT(CompoundTag nbt) { + ((ItemStackHandler) itemHandler).deserializeNBT(nbt); + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/capability/ModeCapabilityManager.java b/src/main/java/nl/requios/effortlessbuilding/capability/ModeCapabilityManager.java index 8002b4e..cc40ab2 100644 --- a/src/main/java/nl/requios/effortlessbuilding/capability/ModeCapabilityManager.java +++ b/src/main/java/nl/requios/effortlessbuilding/capability/ModeCapabilityManager.java @@ -1,104 +1,108 @@ package nl.requios.effortlessbuilding.capability; -import net.minecraft.nbt.NBTBase; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumFacing; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.capabilities.CapabilityInject; -import net.minecraftforge.common.capabilities.ICapabilitySerializable; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.core.Direction; +import net.minecraftforge.common.capabilities.*; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import nl.requios.effortlessbuilding.buildmode.BuildModes; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import static nl.requios.effortlessbuilding.buildmode.ModeSettingsManager.*; +import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager.ModeSettings; @Mod.EventBusSubscriber public class ModeCapabilityManager { - @CapabilityInject(IModeCapability.class) - public final static Capability modeCapability = null; + public static Capability MODE_CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){}); - public interface IModeCapability { - ModeSettings getModeData(); + // Allows for the capability to persist after death. + @SubscribeEvent + public static void clonePlayer(PlayerEvent.Clone event) { + LazyOptional original = event.getOriginal().getCapability(MODE_CAPABILITY, null); + LazyOptional clone = event.getEntity().getCapability(MODE_CAPABILITY, null); + clone.ifPresent(cloneModeCapability -> + original.ifPresent(originalModeCapability -> + cloneModeCapability.setModeData(originalModeCapability.getModeData()))); + } - void setModeData(ModeSettings modeSettings); - } + public interface IModeCapability { + ModeSettings getModeData(); - public static class ModeCapability implements IModeCapability { - private ModeSettings modeSettings; + void setModeData(ModeSettings modeSettings); + } - @Override - public ModeSettings getModeData() { - return modeSettings; - } + public static class ModeCapability implements IModeCapability { + private ModeSettings modeSettings; - @Override - public void setModeData(ModeSettings modeSettings) { - this.modeSettings = modeSettings; - } - } + @Override + public ModeSettings getModeData() { + return modeSettings; + } - public static class Storage implements Capability.IStorage { - @Override - public NBTBase writeNBT(Capability capability, IModeCapability instance, EnumFacing side) { - NBTTagCompound compound = new NBTTagCompound(); - ModeSettings modeSettings = instance.getModeData(); - if (modeSettings == null) modeSettings = new ModeSettings(); + @Override + public void setModeData(ModeSettings modeSettings) { + this.modeSettings = modeSettings; + } + } - //compound.setInteger("buildMode", modeSettings.getBuildMode().ordinal()); + public static class Provider extends CapabilityProvider implements ICapabilitySerializable { - //TODO add mode settings + private IModeCapability instance = new ModeCapability(); + private LazyOptional modeCapabilityOptional = LazyOptional.of(() -> instance); - return compound; - } + public Provider() { + super(Provider.class); + gatherCapabilities(); + } - @Override - public void readNBT(Capability capability, IModeCapability instance, EnumFacing side, NBTBase nbt) { - NBTTagCompound compound = (NBTTagCompound) nbt; + @Nonnull + @Override + public LazyOptional getCapability(@Nonnull Capability cap, @Nullable Direction side) { + if (cap == MODE_CAPABILITY) return modeCapabilityOptional.cast(); + return LazyOptional.empty(); + } - //BuildModes.BuildModeEnum buildMode = BuildModes.BuildModeEnum.values()[compound.getInteger("buildMode")]; + @Override + public void invalidateCaps() { + super.invalidateCaps(); + modeCapabilityOptional.invalidate(); + } - //TODO add mode settings + @Override + public void reviveCaps() { + super.reviveCaps(); + modeCapabilityOptional = LazyOptional.of(() -> instance); + } - ModeSettings modeSettings = new ModeSettings(BuildModes.BuildModeEnum.NORMAL); - instance.setModeData(modeSettings); - } - } + @Override + public Tag serializeNBT() { + CompoundTag compound = new CompoundTag(); + ModeSettings modeSettings = instance.getModeData(); + if (modeSettings == null) modeSettings = new ModeSettings(); - public static class Provider implements ICapabilitySerializable { - IModeCapability inst = modeCapability.getDefaultInstance(); + //compound.putInteger("buildMode", modeSettings.getBuildMode().ordinal()); - @Override - public boolean hasCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { - return capability == modeCapability; - } + //TODO add mode settings - @Override - public T getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { - if (capability == modeCapability) return modeCapability.cast(inst); - return null; - } + return compound; + } - @Override - public NBTBase serializeNBT() { - return modeCapability.getStorage().writeNBT(modeCapability, inst, null); - } + @Override + public void deserializeNBT(Tag nbt) { + CompoundTag compound = (CompoundTag) nbt; - @Override - public void deserializeNBT(NBTBase nbt) { - modeCapability.getStorage().readNBT(modeCapability, inst, null, nbt); - } - } + //BuildModes.BuildModeEnum buildMode = BuildModes.BuildModeEnum.values()[compound.getInteger("buildMode")]; - // Allows for the capability to persist after death. - @SubscribeEvent - public static void clonePlayer(PlayerEvent.Clone event) { - IModeCapability original = event.getOriginal().getCapability(modeCapability, null); - IModeCapability clone = event.getEntity().getCapability(modeCapability, null); - clone.setModeData(original.getModeData()); - } + //TODO add mode settings + + ModeSettings modeSettings = new ModeSettings(BuildModes.BuildModeEnum.NORMAL); + instance.setModeData(modeSettings); + } + + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/capability/ModifierCapabilityManager.java b/src/main/java/nl/requios/effortlessbuilding/capability/ModifierCapabilityManager.java index 183004f..1f69ab0 100644 --- a/src/main/java/nl/requios/effortlessbuilding/capability/ModifierCapabilityManager.java +++ b/src/main/java/nl/requios/effortlessbuilding/capability/ModifierCapabilityManager.java @@ -1,16 +1,16 @@ package nl.requios.effortlessbuilding.capability; -import net.minecraft.nbt.NBTBase; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumFacing; -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.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.core.Direction; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.capabilities.*; +import net.minecraftforge.common.util.INBTSerializable; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import nl.requios.effortlessbuilding.buildmodifier.Array; import nl.requios.effortlessbuilding.buildmodifier.Mirror; import nl.requios.effortlessbuilding.buildmodifier.RadialMirror; @@ -18,164 +18,169 @@ import nl.requios.effortlessbuilding.buildmodifier.RadialMirror; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import static nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager.*; +import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager.ModifierSettings; @Mod.EventBusSubscriber public class ModifierCapabilityManager { - @CapabilityInject(IModifierCapability.class) - public final static Capability modifierCapability = null; + public final static Capability MODIFIER_CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){}); - public interface IModifierCapability { - ModifierSettings getModifierData(); + // Allows for the capability to persist after death. + @SubscribeEvent + public static void clonePlayer(PlayerEvent.Clone event) { + LazyOptional original = event.getOriginal().getCapability(MODIFIER_CAPABILITY, null); + LazyOptional clone = event.getEntity().getCapability(MODIFIER_CAPABILITY, null); + clone.ifPresent(cloneModifierCapability -> + original.ifPresent(originalModifierCapability -> + cloneModifierCapability.setModifierData(originalModifierCapability.getModifierData()))); + } - void setModifierData(ModifierSettings modifierSettings); - } + public interface IModifierCapability { + ModifierSettings getModifierData(); - public static class ModifierCapability implements IModifierCapability { - private ModifierSettings modifierSettings; + void setModifierData(ModifierSettings modifierSettings); + } - @Override - public ModifierSettings getModifierData() { - return modifierSettings; - } + public static class ModifierCapability implements IModifierCapability { + private ModifierSettings modifierSettings; - @Override - public void setModifierData(ModifierSettings modifierSettings) { - this.modifierSettings = modifierSettings; - } - } + @Override + public ModifierSettings getModifierData() { + return modifierSettings; + } - public static class Storage implements Capability.IStorage { - @Override - public NBTBase writeNBT(Capability capability, IModifierCapability instance, EnumFacing side) { - NBTTagCompound compound = new NBTTagCompound(); - ModifierSettings modifierSettings = instance.getModifierData(); - if (modifierSettings == null) modifierSettings = new ModifierSettings(); + @Override + public void setModifierData(ModifierSettings modifierSettings) { + this.modifierSettings = modifierSettings; + } + } - //MIRROR - Mirror.MirrorSettings m = modifierSettings.getMirrorSettings(); - if (m == null) m = new Mirror.MirrorSettings(); - compound.setBoolean("mirrorEnabled", m.enabled); - compound.setDouble("mirrorPosX", m.position.x); - compound.setDouble("mirrorPosY", m.position.y); - compound.setDouble("mirrorPosZ", m.position.z); - compound.setBoolean("mirrorX", m.mirrorX); - compound.setBoolean("mirrorY", m.mirrorY); - compound.setBoolean("mirrorZ", m.mirrorZ); - compound.setInteger("mirrorRadius", m.radius); - compound.setBoolean("mirrorDrawLines", m.drawLines); - compound.setBoolean("mirrorDrawPlanes", m.drawPlanes); + public static class Provider extends CapabilityProvider implements INBTSerializable { - //ARRAY - Array.ArraySettings a = modifierSettings.getArraySettings(); - if (a == null) a = new Array.ArraySettings(); - compound.setBoolean("arrayEnabled", a.enabled); - compound.setInteger("arrayOffsetX", a.offset.getX()); - compound.setInteger("arrayOffsetY", a.offset.getY()); - compound.setInteger("arrayOffsetZ", a.offset.getZ()); - compound.setInteger("arrayCount", a.count); + private final IModifierCapability instance = new ModifierCapability(); + private LazyOptional modifierCapabilityOptional = LazyOptional.of(() -> instance); - compound.setInteger("reachUpgrade", modifierSettings.getReachUpgrade()); + public Provider() { + super(Provider.class); + gatherCapabilities(); + } - //compound.setBoolean("quickReplace", buildSettings.doQuickReplace()); dont save quickreplace + @Nonnull + @Override + public LazyOptional getCapability(@Nonnull Capability cap, @Nullable Direction side) { + if (cap == MODIFIER_CAPABILITY) return modifierCapabilityOptional.cast(); + return LazyOptional.empty(); + } - //RADIAL MIRROR - RadialMirror.RadialMirrorSettings r = modifierSettings.getRadialMirrorSettings(); - if (r == null) r = new RadialMirror.RadialMirrorSettings(); - compound.setBoolean("radialMirrorEnabled", r.enabled); - compound.setDouble("radialMirrorPosX", r.position.x); - compound.setDouble("radialMirrorPosY", r.position.y); - compound.setDouble("radialMirrorPosZ", r.position.z); - compound.setInteger("radialMirrorSlices", r.slices); - compound.setBoolean("radialMirrorAlternate", r.alternate); - compound.setInteger("radialMirrorRadius", r.radius); - compound.setBoolean("radialMirrorDrawLines", r.drawLines); - compound.setBoolean("radialMirrorDrawPlanes", r.drawPlanes); + @Override + public void invalidateCaps() { + super.invalidateCaps(); + modifierCapabilityOptional.invalidate(); + } - return compound; - } + @Override + public void reviveCaps() { + super.reviveCaps(); + modifierCapabilityOptional = LazyOptional.of(() -> instance); + } - @Override - public void readNBT(Capability capability, IModifierCapability instance, EnumFacing side, NBTBase nbt) { - NBTTagCompound compound = (NBTTagCompound) nbt; + @Override + public Tag serializeNBT() { + CompoundTag compound = new CompoundTag(); + ModifierSettings modifierSettings = instance.getModifierData(); + if (modifierSettings == null) modifierSettings = new ModifierSettings(); - //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.getInteger("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); + //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 - boolean arrayEnabled = compound.getBoolean("arrayEnabled"); - BlockPos arrayOffset = new BlockPos( - compound.getInteger("arrayOffsetX"), - compound.getInteger("arrayOffsetY"), - compound.getInteger("arrayOffsetZ")); - int arrayCount = compound.getInteger("arrayCount"); - Array.ArraySettings arraySettings = new Array.ArraySettings(arrayEnabled, arrayOffset, arrayCount); + //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); - int reachUpgrade = compound.getInteger("reachUpgrade"); + compound.putInt("reachUpgrade", modifierSettings.getReachUpgrade()); - //boolean quickReplace = compound.getBoolean("quickReplace"); //dont load quickreplace + //compound.putBoolean("quickReplace", buildSettings.doQuickReplace()); dont save quickreplace - //RADIAL MIRROR - boolean radialMirrorEnabled = compound.getBoolean("radialMirrorEnabled"); - Vec3d radialMirrorPosition = new Vec3d( - compound.getDouble("radialMirrorPosX"), - compound.getDouble("radialMirrorPosY"), - compound.getDouble("radialMirrorPosZ")); - int radialMirrorSlices = compound.getInteger("radialMirrorSlices"); - boolean radialMirrorAlternate = compound.getBoolean("radialMirrorAlternate"); - int radialMirrorRadius = compound.getInteger("radialMirrorRadius"); - boolean radialMirrorDrawLines = compound.getBoolean("radialMirrorDrawLines"); - boolean radialMirrorDrawPlanes = compound.getBoolean("radialMirrorDrawPlanes"); - RadialMirror.RadialMirrorSettings radialMirrorSettings = new RadialMirror.RadialMirrorSettings(radialMirrorEnabled, radialMirrorPosition, - radialMirrorSlices, radialMirrorAlternate, radialMirrorRadius, radialMirrorDrawLines, radialMirrorDrawPlanes); + //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); - ModifierSettings modifierSettings = new ModifierSettings(mirrorSettings, arraySettings, radialMirrorSettings, false, reachUpgrade); - instance.setModifierData(modifierSettings); - } - } + return compound; + } - public static class Provider implements ICapabilitySerializable { - IModifierCapability inst = modifierCapability.getDefaultInstance(); + @Override + public void deserializeNBT(Tag nbt) { + CompoundTag compound = (CompoundTag) nbt; - @Override - public boolean hasCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { - return capability == modifierCapability; - } + //MIRROR + boolean mirrorEnabled = compound.getBoolean("mirrorEnabled"); + Vec3 mirrorPosition = new Vec3( + compound.getDouble("mirrorPosX"), + compound.getDouble("mirrorPosY"), + compound.getDouble("mirrorPosZ")); + boolean mirrorX = compound.getBoolean("mirrorX"); + boolean mirrorY = compound.getBoolean("mirrorY"); + boolean mirrorZ = compound.getBoolean("mirrorZ"); + int mirrorRadius = compound.getInt("mirrorRadius"); + boolean mirrorDrawLines = compound.getBoolean("mirrorDrawLines"); + boolean mirrorDrawPlanes = compound.getBoolean("mirrorDrawPlanes"); + Mirror.MirrorSettings mirrorSettings = new Mirror.MirrorSettings(mirrorEnabled, mirrorPosition, mirrorX, mirrorY, mirrorZ, mirrorRadius, mirrorDrawLines, mirrorDrawPlanes); - @Override - public T getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { - if (capability == modifierCapability) return modifierCapability.cast(inst); - return null; - } + //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); - @Override - public NBTBase serializeNBT() { - return modifierCapability.getStorage().writeNBT(modifierCapability, inst, null); - } + int reachUpgrade = compound.getInt("reachUpgrade"); - @Override - public void deserializeNBT(NBTBase nbt) { - modifierCapability.getStorage().readNBT(modifierCapability, inst, null, nbt); - } - } + //boolean quickReplace = compound.getBoolean("quickReplace"); //dont load quickreplace - // Allows for the capability to persist after death. - @SubscribeEvent - public static void clonePlayer(PlayerEvent.Clone event) { - IModifierCapability original = event.getOriginal().getCapability(modifierCapability, null); - IModifierCapability clone = event.getEntity().getCapability(modifierCapability, null); - clone.setModifierData(original.getModifierData()); - } + //RADIAL MIRROR + boolean radialMirrorEnabled = compound.getBoolean("radialMirrorEnabled"); + Vec3 radialMirrorPosition = new Vec3( + compound.getDouble("radialMirrorPosX"), + compound.getDouble("radialMirrorPosY"), + compound.getDouble("radialMirrorPosZ")); + int radialMirrorSlices = compound.getInt("radialMirrorSlices"); + boolean radialMirrorAlternate = compound.getBoolean("radialMirrorAlternate"); + int radialMirrorRadius = compound.getInt("radialMirrorRadius"); + boolean radialMirrorDrawLines = compound.getBoolean("radialMirrorDrawLines"); + boolean radialMirrorDrawPlanes = compound.getBoolean("radialMirrorDrawPlanes"); + RadialMirror.RadialMirrorSettings radialMirrorSettings = new RadialMirror.RadialMirrorSettings(radialMirrorEnabled, radialMirrorPosition, + radialMirrorSlices, radialMirrorAlternate, radialMirrorRadius, radialMirrorDrawLines, radialMirrorDrawPlanes); + + ModifierSettings modifierSettings = new ModifierSettings(mirrorSettings, arraySettings, radialMirrorSettings, false, reachUpgrade); + instance.setModifierData(modifierSettings); + } + + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/command/CommandReach.java b/src/main/java/nl/requios/effortlessbuilding/command/CommandReach.java deleted file mode 100644 index 0249170..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/command/CommandReach.java +++ /dev/null @@ -1,46 +0,0 @@ -package nl.requios.effortlessbuilding.command; - -import net.minecraft.command.CommandBase; -import net.minecraft.command.CommandException; -import net.minecraft.command.ICommandSender; -import net.minecraft.command.WrongUsageException; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.text.TextComponentString; -import nl.requios.effortlessbuilding.EffortlessBuilding; -import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; -import nl.requios.effortlessbuilding.network.ModifierSettingsMessage; - -public class CommandReach extends CommandBase { - @Override - public String getName() { - return "reach"; - } - - @Override - public String getUsage(ICommandSender sender) { - return "commands.reach.usage"; - } - - @Override - public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { - EntityPlayerMP player = (EntityPlayerMP) sender; - if (args.length != 1) { - int reachUpgrade = ModifierSettingsManager.getModifierSettings(player).getReachUpgrade(); - EffortlessBuilding.log(player, "Current reach: level "+reachUpgrade); - throw new WrongUsageException("commands.reach.usage"); - } - - if (sender instanceof EntityPlayerMP) { - //Set reach level to args[0] - ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - modifierSettings.setReachUpgrade(Integer.valueOf(args[0])); - ModifierSettingsManager.setModifierSettings(player, modifierSettings); - //Send to client - EffortlessBuilding.packetHandler.sendTo(new ModifierSettingsMessage(modifierSettings), player); - - sender.sendMessage(new TextComponentString("Reach level of " + sender.getName() + " set to " + modifierSettings - .getReachUpgrade())); - } - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/compatibility/ActiveChiselsAndBitsProxy.java b/src/main/java/nl/requios/effortlessbuilding/compatibility/ActiveChiselsAndBitsProxy.java deleted file mode 100644 index 667991d..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/compatibility/ActiveChiselsAndBitsProxy.java +++ /dev/null @@ -1,13 +0,0 @@ -package nl.requios.effortlessbuilding.compatibility; - -import mod.chiselsandbits.core.ClientSide; -import mod.chiselsandbits.helpers.ChiselToolType; -import net.minecraft.util.EnumHand; - -public class ActiveChiselsAndBitsProxy implements IChiselsAndBitsProxy { - @Override - public boolean isHoldingChiselTool(EnumHand hand) { - ChiselToolType toolType = ClientSide.instance.getHeldToolType(hand); - return toolType != null && toolType.hasMenu(); - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/compatibility/CompatHelper.java b/src/main/java/nl/requios/effortlessbuilding/compatibility/CompatHelper.java index 4c10d7e..e8ec96e 100644 --- a/src/main/java/nl/requios/effortlessbuilding/compatibility/CompatHelper.java +++ b/src/main/java/nl/requios/effortlessbuilding/compatibility/CompatHelper.java @@ -1,117 +1,63 @@ package nl.requios.effortlessbuilding.compatibility; -import net.minecraft.block.state.IBlockState; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.Item; -import net.minecraft.item.ItemBlock; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumHand; -import net.minecraftforge.fml.common.Loader; -import net.minecraftforge.fml.common.registry.GameRegistry; -import net.minecraftforge.items.CapabilityItemHandler; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; import net.minecraftforge.items.IItemHandler; -import nl.requios.effortlessbuilding.item.ItemRandomizerBag; +import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem; public class CompatHelper { - // 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 void postInit() { - 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) { - Item item = stack.getItem(); - if (item instanceof ItemBlock) - return true; - if ((item instanceof ItemRandomizerBag)) - return true; - if (item == dankNullItem) - return true; - return false; - } + // 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) { + Item item = stack.getItem(); + if (item instanceof BlockItem) + return true; + return item instanceof AbstractRandomizerBagItem; + } - // Get the block to be placed by this proxy. For the /dank/null, it's the slot stack - // pointed to by nbt integer selectedIndex. - public static ItemStack getItemBlockFromStack(ItemStack proxy) { - Item proxyItem = proxy.getItem(); + // Get the block to be placed by this proxy. For the /dank/null, it's the slot stack + // pointed to by nbt integer selectedIndex. + public static ItemStack getItemBlockFromStack(ItemStack proxy) { + Item proxyItem = proxy.getItem(); - if (proxyItem instanceof ItemBlock) - return proxy; + if (proxyItem instanceof BlockItem) + return proxy; - //Randomizer Bag - if (proxyItem instanceof ItemRandomizerBag) { - ItemStack itemStack = proxy; - while (!(itemStack.getItem() instanceof ItemBlock || itemStack.isEmpty())) { - if (itemStack.getItem() instanceof ItemRandomizerBag) - itemStack = ItemRandomizerBag.pickRandomStack(ItemRandomizerBag.getBagInventory(itemStack)); - } - return itemStack; - } + //Randomizer Bag + if (proxyItem instanceof AbstractRandomizerBagItem) { + 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)); + } + } + return itemStack; + } - //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; + } - return ItemStack.EMPTY; - } + public static ItemStack getItemBlockByState(ItemStack stack, BlockState state) { + if (state == null) return ItemStack.EMPTY; - public static ItemStack getItemBlockByState(ItemStack stack, IBlockState state) { - Item blockItem = Item.getItemFromBlock(state.getBlock()); - if (stack.getItem() instanceof ItemBlock) - return stack; - else if (stack.getItem() instanceof ItemRandomizerBag) { - IItemHandler bagInventory = ItemRandomizerBag.getBagInventory(stack); - return ItemRandomizerBag.findStack(bagInventory, blockItem); - } 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; - } + Item blockItem = Item.byBlock(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); + } - // 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, EntityPlayer player) { - //Hacky way to get the origstack, because given origStack is itemblock stack and never a 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); - } - - private static int itemHandlerSlotForItem(ItemStack stack, Item blockItem) { - IItemHandler handler = stack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null); - for (int i = 0; i < handler.getSlots(); i++) { - ItemStack ref = handler.getStackInSlot(i); - if (ref.getItem() instanceof ItemBlock) - if (ref.getItem() == blockItem) - return i; - } - return -1; - } + return ItemStack.EMPTY; + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/compatibility/DummyChiselsAndBitsProxy.java b/src/main/java/nl/requios/effortlessbuilding/compatibility/DummyChiselsAndBitsProxy.java deleted file mode 100644 index 7c37894..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/compatibility/DummyChiselsAndBitsProxy.java +++ /dev/null @@ -1,10 +0,0 @@ -package nl.requios.effortlessbuilding.compatibility; - -import net.minecraft.util.EnumHand; - -public class DummyChiselsAndBitsProxy implements IChiselsAndBitsProxy { - @Override - public boolean isHoldingChiselTool(EnumHand hand) { - return false; - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/compatibility/IChiselsAndBitsProxy.java b/src/main/java/nl/requios/effortlessbuilding/compatibility/IChiselsAndBitsProxy.java deleted file mode 100644 index ec288ba..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/compatibility/IChiselsAndBitsProxy.java +++ /dev/null @@ -1,7 +0,0 @@ -package nl.requios.effortlessbuilding.compatibility; - -import net.minecraft.util.EnumHand; - -public interface IChiselsAndBitsProxy { - boolean isHoldingChiselTool(EnumHand hand); -} diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/DiamondRandomizerBagContainer.java b/src/main/java/nl/requios/effortlessbuilding/gui/DiamondRandomizerBagContainer.java new file mode 100644 index 0000000..8e4c50c --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/gui/DiamondRandomizerBagContainer.java @@ -0,0 +1,148 @@ +package nl.requios.effortlessbuilding.gui; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.InteractionHand; +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.ClickType; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.ItemStackHandler; +import net.minecraftforge.items.SlotItemHandler; +import nl.requios.effortlessbuilding.EffortlessBuilding; +import nl.requios.effortlessbuilding.item.DiamondRandomizerBagItem; + +public class DiamondRandomizerBagContainer extends AbstractContainerMenu { + + private static final int INV_START = DiamondRandomizerBagItem.INV_SIZE, + INV_END = INV_START + 26, + HOTBAR_START = INV_END + 1, + HOTBAR_END = HOTBAR_START + 8; + private final IItemHandler bagInventory; + + public DiamondRandomizerBagContainer(MenuType type, int id){ + super(type, id); + bagInventory = null; + } + + //Client + public DiamondRandomizerBagContainer(int id, Inventory playerInventory, FriendlyByteBuf packetBuffer) { + this(id, playerInventory); + } + + //Server? + public DiamondRandomizerBagContainer(int containerId, Inventory playerInventory) { + this(containerId, playerInventory, new ItemStackHandler(DiamondRandomizerBagItem.INV_SIZE)); + } + + public DiamondRandomizerBagContainer(int containerId, Inventory playerInventory, IItemHandler inventory) { + super(EffortlessBuilding.DIAMOND_RANDOMIZER_BAG_CONTAINER.get(), containerId); + bagInventory = inventory; + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.addSlot(new SlotItemHandler(bagInventory, j + i * 9, 8 + j * 18, 18 + i * 18)); + } + } + + // add player inventory slots + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + addSlot(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + // add hotbar slots + for (int i = 0; i < 9; ++i) { + addSlot(new Slot(playerInventory, i, 8 + i * 18, 142)); + } + } + + @Override + public boolean stillValid(Player playerIn) { + return true; + } + + @Override + public Slot getSlot(int parSlotIndex) { + if (parSlotIndex >= slots.size()) + parSlotIndex = slots.size() - 1; + return super.getSlot(parSlotIndex); + } + + @Override + public ItemStack quickMoveStack(Player playerIn, int slotIndex) { + ItemStack itemstack = ItemStack.EMPTY; + Slot slot = this.slots.get(slotIndex); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + itemstack = itemstack1.copy(); + + // If item is in our custom inventory + if (slotIndex < INV_START) { + // try to place in player inventory / action bar + if (!this.moveItemStackTo(itemstack1, INV_START, HOTBAR_END + 1, true)) { + return ItemStack.EMPTY; + } + + slot.onQuickCraft(itemstack1, itemstack); + } + // Item is in inventory / hotbar, try to place in custom inventory or armor slots + else { + /** + * Implementation number 1: Shift-click into your custom inventory + */ + if (slotIndex >= INV_START) { + // place in custom inventory + if (!this.moveItemStackTo(itemstack1, 0, INV_START, false)) { + return ItemStack.EMPTY; + } + } + } + + if (itemstack1.getCount() == 0) { + slot.set(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.EMPTY; + } + + slot.onTake(playerIn, itemstack1); + } + + return itemstack; + } + + /** + * You should override this method to prevent the player from moving the stack that + * opened the inventory, otherwise if the player moves it, the inventory will not + * be able to save properly + */ + @Override + public void clicked(int slot, int dragType, ClickType clickTypeIn, Player player) { + // this will prevent the player from interacting with the item that opened the inventory: + if (slot >= 0 && getSlot(slot) != null && getSlot(slot).getItem().equals(player.getItemInHand(InteractionHand.MAIN_HAND))) { + //Do nothing; + return; + } + super.clicked(slot, dragType, clickTypeIn, player); + } + + /** + * Callback for when the crafting gui is closed. + */ + @Override + public void removed(Player player) { + super.removed(player); + if (!player.level.isClientSide) { + broadcastChanges(); + } + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/DiamondRandomizerBagScreen.java b/src/main/java/nl/requios/effortlessbuilding/gui/DiamondRandomizerBagScreen.java new file mode 100644 index 0000000..520623f --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/gui/DiamondRandomizerBagScreen.java @@ -0,0 +1,49 @@ +package nl.requios.effortlessbuilding.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import nl.requios.effortlessbuilding.EffortlessBuilding; + +import javax.annotation.ParametersAreNonnullByDefault; + +@OnlyIn(Dist.CLIENT) +@ParametersAreNonnullByDefault +public class DiamondRandomizerBagScreen extends AbstractContainerScreen { + private Inventory inventory; + + private static final ResourceLocation guiTextures = new ResourceLocation(EffortlessBuilding.MODID, "textures/gui/container/diamondrandomizerbag.png"); + + public DiamondRandomizerBagScreen(DiamondRandomizerBagContainer randomizerBagContainer, Inventory playerInventory, Component title) { + super(randomizerBagContainer, playerInventory, title); + this.inventory = playerInventory; + imageHeight = 167; + } + + @Override + public void render(PoseStack ms, int mouseX, int mouseY, float partialTicks) { + renderBackground(ms); + super.render(ms, mouseX, mouseY, partialTicks); + this.renderTooltip(ms, mouseX, mouseY); + } + + @Override + protected void renderLabels(PoseStack ms, int mouseX, int mouseY) { + this.font.draw(ms, this.title, 8, 6, 0x404040); + this.font.draw(ms, this.playerInventoryTitle, 8, imageHeight - 96 + 2, 0x404040); + } + + @Override + protected void renderBg(PoseStack ms, float partialTicks, int mouseX, int mouseY) { + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1f); + RenderSystem.setShaderTexture(0, guiTextures); + int marginHorizontal = (width - imageWidth) / 2; + int marginVertical = (height - imageHeight) / 2; + blit(ms, marginHorizontal, marginVertical, 0, 0, imageWidth, imageHeight); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/GoldenRandomizerBagContainer.java b/src/main/java/nl/requios/effortlessbuilding/gui/GoldenRandomizerBagContainer.java new file mode 100644 index 0000000..cb0e4ff --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/gui/GoldenRandomizerBagContainer.java @@ -0,0 +1,146 @@ +package nl.requios.effortlessbuilding.gui; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.InteractionHand; +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.ClickType; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.ItemStackHandler; +import net.minecraftforge.items.SlotItemHandler; +import nl.requios.effortlessbuilding.EffortlessBuilding; +import nl.requios.effortlessbuilding.item.GoldenRandomizerBagItem; + +public class GoldenRandomizerBagContainer extends AbstractContainerMenu { + + private static final int INV_START = GoldenRandomizerBagItem.INV_SIZE, + INV_END = INV_START + 26, + HOTBAR_START = INV_END + 1, + HOTBAR_END = HOTBAR_START + 8; + private final IItemHandler bagInventory; + + public GoldenRandomizerBagContainer(MenuType type, int id){ + super(type, id); + bagInventory = null; + } + + //Client + public GoldenRandomizerBagContainer(int id, Inventory playerInventory, FriendlyByteBuf packetBuffer) { + this(id, playerInventory); + } + + //Server? + public GoldenRandomizerBagContainer(int containerId, Inventory playerInventory) { + this(containerId, playerInventory, new ItemStackHandler(GoldenRandomizerBagItem.INV_SIZE)); + } + + public GoldenRandomizerBagContainer(int containerId, Inventory playerInventory, IItemHandler inventory) { + super(EffortlessBuilding.GOLDEN_RANDOMIZER_BAG_CONTAINER.get(), containerId); + bagInventory = inventory; + + for (int i = 0; i < GoldenRandomizerBagItem.INV_SIZE; ++i) { + this.addSlot(new SlotItemHandler(bagInventory, i, 8 + (18 * i), 20)); + } + + // add player inventory slots + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + addSlot(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, 51 + i * 18)); + } + } + + // add hotbar slots + for (int i = 0; i < 9; ++i) { + addSlot(new Slot(playerInventory, i, 8 + i * 18, 109)); + } + } + + @Override + public boolean stillValid(Player playerIn) { + return true; + } + + @Override + public Slot getSlot(int parSlotIndex) { + if (parSlotIndex >= slots.size()) + parSlotIndex = slots.size() - 1; + return super.getSlot(parSlotIndex); + } + + @Override + public ItemStack quickMoveStack(Player playerIn, int slotIndex) { + ItemStack itemstack = ItemStack.EMPTY; + Slot slot = this.slots.get(slotIndex); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + itemstack = itemstack1.copy(); + + // If item is in our custom inventory + if (slotIndex < INV_START) { + // try to place in player inventory / action bar + if (!this.moveItemStackTo(itemstack1, INV_START, HOTBAR_END + 1, true)) { + return ItemStack.EMPTY; + } + + slot.onQuickCraft(itemstack1, itemstack); + } + // Item is in inventory / hotbar, try to place in custom inventory or armor slots + else { + /** + * Implementation number 1: Shift-click into your custom inventory + */ + if (slotIndex >= INV_START) { + // place in custom inventory + if (!this.moveItemStackTo(itemstack1, 0, INV_START, false)) { + return ItemStack.EMPTY; + } + } + } + + if (itemstack1.getCount() == 0) { + slot.set(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.EMPTY; + } + + slot.onTake(playerIn, itemstack1); + } + + return itemstack; + } + + /** + * You should override this method to prevent the player from moving the stack that + * opened the inventory, otherwise if the player moves it, the inventory will not + * be able to save properly + */ + @Override + public void clicked(int slot, int dragType, ClickType clickTypeIn, Player player) { + // this will prevent the player from interacting with the item that opened the inventory: + if (slot >= 0 && getSlot(slot) != null && getSlot(slot).getItem().equals(player.getItemInHand(InteractionHand.MAIN_HAND))) { + //Do nothing; + return; + } + super.clicked(slot, dragType, clickTypeIn, player); + } + + /** + * Callback for when the crafting gui is closed. + */ + @Override + public void removed(Player player) { + super.removed(player); + if (!player.level.isClientSide) { + broadcastChanges(); + } + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/GoldenRandomizerBagScreen.java b/src/main/java/nl/requios/effortlessbuilding/gui/GoldenRandomizerBagScreen.java new file mode 100644 index 0000000..45a68b7 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/gui/GoldenRandomizerBagScreen.java @@ -0,0 +1,49 @@ +package nl.requios.effortlessbuilding.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import nl.requios.effortlessbuilding.EffortlessBuilding; + +import javax.annotation.ParametersAreNonnullByDefault; + +@OnlyIn(Dist.CLIENT) +@ParametersAreNonnullByDefault +public class GoldenRandomizerBagScreen extends AbstractContainerScreen { + private Inventory inventory; + + private static final ResourceLocation guiTextures = new ResourceLocation(EffortlessBuilding.MODID, "textures/gui/container/goldenrandomizerbag.png"); + + public GoldenRandomizerBagScreen(GoldenRandomizerBagContainer randomizerBagContainer, Inventory playerInventory, Component title) { + super(randomizerBagContainer, playerInventory, title); + this.inventory = playerInventory; + imageHeight = 134; + } + + @Override + public void render(PoseStack ms, int mouseX, int mouseY, float partialTicks) { + renderBackground(ms); + super.render(ms, mouseX, mouseY, partialTicks); + this.renderTooltip(ms, mouseX, mouseY); + } + + @Override + protected void renderLabels(PoseStack ms, int mouseX, int mouseY) { + this.font.draw(ms, this.title, 8, 6, 0x404040); + this.font.draw(ms, this.playerInventoryTitle, 8, imageHeight - 96 + 2, 0x404040); + } + + @Override + protected void renderBg(PoseStack ms, float partialTicks, int mouseX, int mouseY) { + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1f); + RenderSystem.setShaderTexture(0, guiTextures); + int marginHorizontal = (width - imageWidth) / 2; + int marginVertical = (height - imageHeight) / 2; + blit(ms, marginHorizontal, marginVertical, 0, 0, imageWidth, imageHeight); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/RandomizerBagContainer.java b/src/main/java/nl/requios/effortlessbuilding/gui/RandomizerBagContainer.java index b5f3432..32a6dbc 100644 --- a/src/main/java/nl/requios/effortlessbuilding/gui/RandomizerBagContainer.java +++ b/src/main/java/nl/requios/effortlessbuilding/gui/RandomizerBagContainer.java @@ -1,130 +1,146 @@ package nl.requios.effortlessbuilding.gui; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.InventoryPlayer; -import net.minecraft.inventory.ClickType; -import net.minecraft.inventory.Container; -import net.minecraft.inventory.Slot; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.InteractionHand; import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.ItemStackHandler; import net.minecraftforge.items.SlotItemHandler; -import nl.requios.effortlessbuilding.item.ItemRandomizerBag; +import nl.requios.effortlessbuilding.EffortlessBuilding; +import nl.requios.effortlessbuilding.item.RandomizerBagItem; -public class RandomizerBagContainer extends Container { +public class RandomizerBagContainer extends AbstractContainerMenu { - private final IItemHandler bagInventory; - private final int sizeInventory; + private static final int INV_START = RandomizerBagItem.INV_SIZE, + INV_END = INV_START + 26, + HOTBAR_START = INV_END + 1, + HOTBAR_END = HOTBAR_START + 8; + private final IItemHandler bagInventory; - private static final int INV_START = ItemRandomizerBag.INV_SIZE, INV_END = INV_START + 26, - HOTBAR_START = INV_END + 1, HOTBAR_END = HOTBAR_START + 8; + public RandomizerBagContainer(MenuType type, int id){ + super(type, id); + bagInventory = null; + } - public RandomizerBagContainer(InventoryPlayer parInventoryPlayer, IItemHandler parIInventory) { - bagInventory = parIInventory; - sizeInventory = bagInventory.getSlots(); - for (int i = 0; i < sizeInventory; ++i) { - this.addSlotToContainer(new SlotItemHandler(bagInventory, i, 44 + (18 * i), 20)); - } + //Client + public RandomizerBagContainer(int id, Inventory playerInventory, FriendlyByteBuf packetBuffer) { + this(id, playerInventory); + } - // add player inventory slots - int i; - for (i = 0; i < 3; ++i) { - for (int j = 0; j < 9; ++j) { - addSlotToContainer(new Slot(parInventoryPlayer, j + i * 9 + 9, 8 + j * 18, 51 + i * 18)); - } - } + //Server? + public RandomizerBagContainer(int containerId, Inventory playerInventory) { + this(containerId, playerInventory, new ItemStackHandler(RandomizerBagItem.INV_SIZE)); + } - // add hotbar slots - for (i = 0; i < 9; ++i) { - addSlotToContainer(new Slot(parInventoryPlayer, i, 8 + i * 18, 109)); - } - } + public RandomizerBagContainer(int containerId, Inventory playerInventory, IItemHandler inventory) { + super(EffortlessBuilding.RANDOMIZER_BAG_CONTAINER.get(), containerId); + bagInventory = inventory; + + for (int i = 0; i < RandomizerBagItem.INV_SIZE; ++i) { + this.addSlot(new SlotItemHandler(bagInventory, i, 44 + (18 * i), 20)); + } + + // add player inventory slots + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + addSlot(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, 51 + i * 18)); + } + } + + // add hotbar slots + for (int i = 0; i < 9; ++i) { + addSlot(new Slot(playerInventory, i, 8 + i * 18, 109)); + } + } @Override - public boolean canInteractWith(EntityPlayer playerIn) { - return true; - } + public boolean stillValid(Player playerIn) { + return true; + } - @Override - public Slot getSlot(int parSlotIndex) - { - if(parSlotIndex >= inventorySlots.size()) - parSlotIndex = inventorySlots.size() - 1; - return super.getSlot(parSlotIndex); - } + @Override + public Slot getSlot(int parSlotIndex) { + if (parSlotIndex >= slots.size()) + parSlotIndex = slots.size() - 1; + return super.getSlot(parSlotIndex); + } - @Override - public ItemStack transferStackInSlot(EntityPlayer playerIn, int slotIndex) { - ItemStack itemstack = ItemStack.EMPTY; - Slot slot = this.inventorySlots.get(slotIndex); + @Override + public ItemStack quickMoveStack(Player playerIn, int slotIndex) { + ItemStack itemstack = ItemStack.EMPTY; + Slot slot = this.slots.get(slotIndex); - if (slot != null && slot.getHasStack()) { - ItemStack itemstack1 = slot.getStack(); - itemstack = itemstack1.copy(); + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + itemstack = itemstack1.copy(); - // If item is in our custom inventory - if (slotIndex < INV_START) { - // try to place in player inventory / action bar - if (!this.mergeItemStack(itemstack1, INV_START, HOTBAR_END + 1, true)) { - return ItemStack.EMPTY; - } + // If item is in our custom inventory + if (slotIndex < INV_START) { + // try to place in player inventory / action bar + if (!this.moveItemStackTo(itemstack1, INV_START, HOTBAR_END + 1, true)) { + return ItemStack.EMPTY; + } - slot.onSlotChange(itemstack1, itemstack); - } - // Item is in inventory / hotbar, try to place in custom inventory or armor slots - else { - /** - * Implementation number 1: Shift-click into your custom inventory - */ - if (slotIndex >= INV_START) { - // place in custom inventory - if (!this.mergeItemStack(itemstack1, 0, INV_START, false)) { - return ItemStack.EMPTY; - } - } + slot.onQuickCraft(itemstack1, itemstack); + } + // Item is in inventory / hotbar, try to place in custom inventory or armor slots + else { + /** + * Implementation number 1: Shift-click into your custom inventory + */ + if (slotIndex >= INV_START) { + // place in custom inventory + if (!this.moveItemStackTo(itemstack1, 0, INV_START, false)) { + return ItemStack.EMPTY; + } + } + } - } + if (itemstack1.getCount() == 0) { + slot.set(ItemStack.EMPTY); + } else { + slot.setChanged(); + } - if (itemstack1.getCount() == 0) { - slot.putStack(ItemStack.EMPTY); - } else { - slot.onSlotChanged(); - } + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.EMPTY; + } - if (itemstack1.getCount() == itemstack.getCount()) { - return ItemStack.EMPTY; - } + slot.onTake(playerIn, itemstack1); + } - slot.onTake(playerIn, itemstack1); - } + return itemstack; + } - return itemstack; - } + /** + * You should override this method to prevent the player from moving the stack that + * opened the inventory, otherwise if the player moves it, the inventory will not + * be able to save properly + */ + @Override + public void clicked(int slot, int dragType, ClickType clickTypeIn, Player player) { + // this will prevent the player from interacting with the item that opened the inventory: + if (slot >= 0 && getSlot(slot) != null && getSlot(slot).getItem().equals(player.getItemInHand(InteractionHand.MAIN_HAND))) { + //Do nothing; + return; + } + super.clicked(slot, dragType, clickTypeIn, player); + } - /** - * You should override this method to prevent the player from moving the stack that - * opened the inventory, otherwise if the player moves it, the inventory will not - * be able to save properly - */ - @Override - public ItemStack slotClick(int slot, int dragType, ClickType clickTypeIn, EntityPlayer player) { - // this will prevent the player from interacting with the item that opened the inventory: - if (slot >= 0 && getSlot(slot) != null && getSlot(slot).getStack().equals(player.getHeldItem(EnumHand.MAIN_HAND))) { - return ItemStack.EMPTY; - } - return super.slotClick(slot, dragType, clickTypeIn, player); - } - - /** - * Callback for when the crafting gui is closed. - */ - @Override - public void onContainerClosed(EntityPlayer player) - { - super.onContainerClosed(player); - if(!player.world.isRemote) - { - detectAndSendChanges(); - } - } + /** + * Callback for when the crafting gui is closed. + */ + @Override + public void removed(Player player) { + super.removed(player); + if (!player.level.isClientSide) { + broadcastChanges(); + } + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/RandomizerBagGuiContainer.java b/src/main/java/nl/requios/effortlessbuilding/gui/RandomizerBagGuiContainer.java deleted file mode 100644 index 3777817..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/gui/RandomizerBagGuiContainer.java +++ /dev/null @@ -1,49 +0,0 @@ -package nl.requios.effortlessbuilding.gui; - -import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.entity.player.InventoryPlayer; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -import net.minecraftforge.items.IItemHandler; -import nl.requios.effortlessbuilding.EffortlessBuilding; - -@SideOnly(Side.CLIENT) -public class RandomizerBagGuiContainer extends GuiContainer { - private static final ResourceLocation guiTextures = - new ResourceLocation(EffortlessBuilding.MODID, "textures/gui/container/randomizerbag.png"); - private final InventoryPlayer inventoryPlayer; - private final IItemHandler inventoryBag; - - public RandomizerBagGuiContainer(InventoryPlayer inventoryPlayer, IItemHandler inventoryBag) { - super(new RandomizerBagContainer(inventoryPlayer, inventoryBag)); - this.inventoryPlayer = inventoryPlayer; - this.inventoryBag = inventoryBag; - - ySize = 134; - } - - @Override - public void drawScreen(int mouseX, int mouseY, float partialTicks) { - drawDefaultBackground(); - super.drawScreen(mouseX, mouseY, partialTicks); - this.renderHoveredToolTip(mouseX, mouseY); - } - - @Override - protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { - String s = "Randomizer Bag"; - fontRenderer.drawString(s, 8, 6, 0x404040); - fontRenderer.drawString(inventoryPlayer.getDisplayName().getUnformattedText(), 8, ySize - 96 + 2, 0x404040); - } - - @Override - protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) { - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - mc.getTextureManager().bindTexture(guiTextures); - int marginHorizontal = (width - xSize) / 2; - int marginVertical = (height - ySize) / 2; - drawTexturedModalRect(marginHorizontal, marginVertical, 0, 0, xSize, ySize); - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/RandomizerBagGuiHandler.java b/src/main/java/nl/requios/effortlessbuilding/gui/RandomizerBagGuiHandler.java deleted file mode 100644 index ded862c..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/gui/RandomizerBagGuiHandler.java +++ /dev/null @@ -1,41 +0,0 @@ -package nl.requios.effortlessbuilding.gui; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.world.World; -import net.minecraftforge.fml.common.network.IGuiHandler; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -import net.minecraftforge.items.CapabilityItemHandler; -import net.minecraftforge.items.IItemHandler; -import nl.requios.effortlessbuilding.EffortlessBuilding; - -import javax.annotation.Nullable; - -public class RandomizerBagGuiHandler implements IGuiHandler { - @Nullable - @Override - public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) { - if (ID == EffortlessBuilding.RANDOMIZER_BAG_GUI) { - // Use the player's held item to create the container - IItemHandler capability = player.getHeldItemMainhand().hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null) ? - player.getHeldItemMainhand().getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null) : - player.getHeldItemOffhand().getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null); - return new RandomizerBagContainer(player.inventory, capability); - } - return null; - } - - @Nullable - @Override - @SideOnly(Side.CLIENT) - public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) { - if (ID == EffortlessBuilding.RANDOMIZER_BAG_GUI) { - // Use the player's held item to create the client-side gui container - IItemHandler capability = player.getHeldItemMainhand().hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null) ? - player.getHeldItemMainhand().getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null) : - player.getHeldItemOffhand().getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null); - return new RandomizerBagGuiContainer(player.inventory, capability); - } - return null; - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/RandomizerBagScreen.java b/src/main/java/nl/requios/effortlessbuilding/gui/RandomizerBagScreen.java new file mode 100644 index 0000000..44aa710 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/gui/RandomizerBagScreen.java @@ -0,0 +1,49 @@ +package nl.requios.effortlessbuilding.gui; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.network.chat.Component; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import nl.requios.effortlessbuilding.EffortlessBuilding; + +import javax.annotation.ParametersAreNonnullByDefault; + +@OnlyIn(Dist.CLIENT) +@ParametersAreNonnullByDefault +public class RandomizerBagScreen extends AbstractContainerScreen { + private Inventory inventory; + + private static final ResourceLocation guiTextures = new ResourceLocation(EffortlessBuilding.MODID, "textures/gui/container/randomizerbag.png"); + + public RandomizerBagScreen(RandomizerBagContainer randomizerBagContainer, Inventory playerInventory, Component title) { + super(randomizerBagContainer, playerInventory, title); + this.inventory = playerInventory; + imageHeight = 134; + } + + @Override + public void render(PoseStack ms, int mouseX, int mouseY, float partialTicks) { + renderBackground(ms); + super.render(ms, mouseX, mouseY, partialTicks); + this.renderTooltip(ms, mouseX, mouseY); + } + + @Override + protected void renderLabels(PoseStack ms, int mouseX, int mouseY) { + this.font.draw(ms, this.title, 8, 6, 0x404040); + this.font.draw(ms, this.playerInventoryTitle, 8, imageHeight - 96 + 2, 0x404040); + } + + @Override + protected void renderBg(PoseStack ms, float partialTicks, int mouseX, int mouseY) { + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1f); + RenderSystem.setShaderTexture(0, guiTextures); + int marginHorizontal = (width - imageWidth) / 2; + int marginVertical = (height - imageHeight) / 2; + blit(ms, marginHorizontal, marginVertical, 0, 0, imageWidth, imageHeight); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/PlayerSettingsGui.java b/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/PlayerSettingsGui.java new file mode 100644 index 0000000..27c34a0 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/PlayerSettingsGui.java @@ -0,0 +1,309 @@ +package nl.requios.effortlessbuilding.gui.buildmode; + +import com.mojang.blaze3d.vertex.*; +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.client.Minecraft; +import net.minecraft.client.resources.sounds.SimpleSoundInstance; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.ObjectSelectionList; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.util.Mth; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.gui.widget.ExtendedButton; +import net.minecraftforge.client.gui.widget.Slider; +import nl.requios.effortlessbuilding.EffortlessBuilding; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class PlayerSettingsGui extends Screen { + + protected int left, right, top, bottom; + protected boolean showShaderList = false; + private Button shaderTypeButton; + private ShaderTypeList shaderTypeList; + private Button closeButton; + + public PlayerSettingsGui() { + super(new TranslatableComponent("effortlessbuilding.screen.player_settings")); + } + + @Override + protected void init() { + left = this.width / 2 - 140; + right = this.width / 2 + 140; + top = this.height / 2 - 100; + bottom = this.height / 2 + 100; + + int yy = top; + shaderTypeList = new ShaderTypeList(this.minecraft); + addWidget(shaderTypeList); + //TODO set selected name + Component currentShaderName = ShaderType.DISSOLVE_BLUE.name; + shaderTypeButton = new ExtendedButton(right - 180, yy, 180, 20, currentShaderName, (button) -> { + showShaderList = !showShaderList; + }); + addRenderableOnly(shaderTypeButton); + + yy += 50; + Slider slider = new Slider(right - 200, yy, 200, 20, TextComponent.EMPTY, TextComponent.EMPTY, 0.5, 2.0, 1.0, true, true, (button) -> { + + }); + addRenderableOnly(slider); + + closeButton = new ExtendedButton(left + 50, bottom - 20, 180, 20, new TextComponent("Done"), (button) -> this.minecraft.player.closeContainer()); + addRenderableOnly(closeButton); + } + + @Override + public void tick() { + super.tick(); + } + + @Override + public void render(PoseStack ms, int mouseX, int mouseY, float partialTicks) { + this.renderBackground(ms); + + int yy = top; + font.draw(ms, "Shader type", left, yy + 5, 0xFFFFFF); + + yy += 50; + font.draw(ms, "Shader speed", left, yy + 5, 0xFFFFFF); + + super.render(ms, mouseX, mouseY, partialTicks); + + if (showShaderList) + this.shaderTypeList.render(ms, mouseX, mouseY, partialTicks); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) { + super.mouseClicked(mouseX, mouseY, mouseButton); + if (showShaderList) { + if (!shaderTypeList.isMouseOver(mouseX, mouseY) && !shaderTypeButton.isMouseOver(mouseX, mouseY)) + showShaderList = false; + } + return true; + } + + @Override + public void removed() { + ShaderTypeList.ShaderTypeEntry selectedShader = shaderTypeList.getSelected(); + //TODO save and remove + } + + public enum ShaderType { + DISSOLVE_BLUE("Dissolve Blue"), + DISSOLVE_ORANGE("Dissolve Orange"); + + public Component name; + + ShaderType(Component name) { + this.name = name; + } + + ShaderType(String name) { + this.name = new TextComponent(name); + } + } + + //Inspired by LanguageScreen + @OnlyIn(Dist.CLIENT) + class ShaderTypeList extends ObjectSelectionList { + + public ShaderTypeList(Minecraft mcIn) { + super(mcIn, 180, 140, top + 20, top + 100, 18); + this.setLeftPos(right - width); + + for (int i = 0; i < 40; i++) { + + for (ShaderType shaderType : ShaderType.values()) { + ShaderTypeEntry shaderTypeEntry = new ShaderTypeEntry(shaderType); + addEntry(shaderTypeEntry); + //TODO setSelected to this if appropriate + } + + } + + if (this.getSelected() != null) { + this.centerScrollOn(this.getSelected()); + } + } + + @Override + public int getRowWidth() { + return width; + } + + @Override + public void setSelected(PlayerSettingsGui.ShaderTypeList.ShaderTypeEntry selected) { + super.setSelected(selected); + Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); + EffortlessBuilding.log("Selected shader " + selected.shaderType.name); + shaderTypeButton.setMessage(selected.shaderType.name); +// showShaderList = false; + } + + @Override + public boolean mouseClicked(double p_mouseClicked_1_, double p_mouseClicked_3_, int p_mouseClicked_5_) { + if (!showShaderList) return false; + return super.mouseClicked(p_mouseClicked_1_, p_mouseClicked_3_, p_mouseClicked_5_); + } + + @Override + public boolean mouseReleased(double p_mouseReleased_1_, double p_mouseReleased_3_, int p_mouseReleased_5_) { + if (!showShaderList) return false; + return super.mouseReleased(p_mouseReleased_1_, p_mouseReleased_3_, p_mouseReleased_5_); + } + + @Override + public boolean mouseDragged(double p_mouseDragged_1_, double p_mouseDragged_3_, int p_mouseDragged_5_, double p_mouseDragged_6_, double p_mouseDragged_8_) { + if (!showShaderList) return false; + return super.mouseDragged(p_mouseDragged_1_, p_mouseDragged_3_, p_mouseDragged_5_, p_mouseDragged_6_, p_mouseDragged_8_); + } + + @Override + public boolean mouseScrolled(double p_mouseScrolled_1_, double p_mouseScrolled_3_, double p_mouseScrolled_5_) { + if (!showShaderList) return false; + return super.mouseScrolled(p_mouseScrolled_1_, p_mouseScrolled_3_, p_mouseScrolled_5_); + } + + @Override + public boolean isMouseOver(double p_isMouseOver_1_, double p_isMouseOver_3_) { + if (!showShaderList) return false; + return super.isMouseOver(p_isMouseOver_1_, p_isMouseOver_3_); + } + + protected boolean isFocused() { + return PlayerSettingsGui.this.getFocused() == this; + } + + @Override + protected int getScrollbarPosition() { + return right - 6; + } + + //From AbstractList, disabled parts + @Override + public void render(PoseStack ms, int p_render_1_, int p_render_2_, float p_render_3_) { + this.renderBackground(ms); + int i = this.getScrollbarPosition(); + int j = i + 6; + Tesselator tessellator = Tesselator.getInstance(); + BufferBuilder bufferbuilder = tessellator.getBuilder(); +// this.minecraft.getTextureManager().bindTexture(AbstractGui.BACKGROUND_LOCATION); + RenderSystem.enableBlend(); + RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ZERO, GlStateManager.DestFactor.ONE); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + float f = 32.0F; + bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); + bufferbuilder.vertex(this.x0, this.y1, 0.0D).color(20, 20, 20, 180).endVertex(); + bufferbuilder.vertex(this.x1, this.y1, 0.0D).color(20, 20, 20, 180).endVertex(); + bufferbuilder.vertex(this.x1, this.y0, 0.0D).color(20, 20, 20, 180).endVertex(); + bufferbuilder.vertex(this.x0, this.y0, 0.0D).color(20, 20, 20, 180).endVertex(); + tessellator.end(); + int k = this.getRowLeft(); + int l = this.y0 + 4 - (int) this.getScrollAmount(); + if (this.renderHeader) { + this.renderHeader(ms, k, l, tessellator); + } + + this.renderList(ms, k, l, p_render_1_, p_render_2_, p_render_3_); + RenderSystem.disableDepthTest(); +// this.renderHoleBackground(0, this.y0, 255, 255); +// this.renderHoleBackground(this.y1, this.height, 255, 255); + RenderSystem.enableBlend(); + RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ZERO, GlStateManager.DestFactor.ONE); + RenderSystem.disableTexture(); +// int i1 = 4; +// bufferbuilder.begin(7, DefaultVertexFormats.POSITION_TEX_COLOR); +// bufferbuilder.pos((double)this.x0, (double)(this.y0 + 4), 0.0D).tex(0.0F, 1.0F).color(0, 0, 0, 0).endVertex(); +// bufferbuilder.pos((double)this.x1, (double)(this.y0 + 4), 0.0D).tex(1.0F, 1.0F).color(0, 0, 0, 0).endVertex(); +// bufferbuilder.pos((double)this.x1, (double)this.y0, 0.0D).tex(1.0F, 0.0F).color(0, 0, 0, 255).endVertex(); +// bufferbuilder.pos((double)this.x0, (double)this.y0, 0.0D).tex(0.0F, 0.0F).color(0, 0, 0, 255).endVertex(); +// tessellator.draw(); +// bufferbuilder.begin(7, DefaultVertexFormats.POSITION_TEX_COLOR); +// bufferbuilder.pos((double)this.x0, (double)this.y1, 0.0D).tex(0.0F, 1.0F).color(0, 0, 0, 255).endVertex(); +// bufferbuilder.pos((double)this.x1, (double)this.y1, 0.0D).tex(1.0F, 1.0F).color(0, 0, 0, 255).endVertex(); +// bufferbuilder.pos((double)this.x1, (double)(this.y1 - 4), 0.0D).tex(1.0F, 0.0F).color(0, 0, 0, 0).endVertex(); +// bufferbuilder.pos((double)this.x0, (double)(this.y1 - 4), 0.0D).tex(0.0F, 0.0F).color(0, 0, 0, 0).endVertex(); +// tessellator.draw(); + + //SCROLLBAR + int j1 = this.getMaxScroll(); + if (j1 > 0) { + int k1 = (int) ((float) ((this.y1 - this.y0) * (this.y1 - this.y0)) / (float) this.getMaxPosition()); + k1 = Mth.clamp(k1, 32, this.y1 - this.y0 - 8); + int l1 = (int) this.getScrollAmount() * (this.y1 - this.y0 - k1) / j1 + this.y0; + if (l1 < this.y0) { + l1 = this.y0; + } + + bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); + bufferbuilder.vertex(i, this.y1, 0.0D).uv(0.0F, 1.0F).color(0, 0, 0, 255).endVertex(); + bufferbuilder.vertex(j, this.y1, 0.0D).uv(1.0F, 1.0F).color(0, 0, 0, 255).endVertex(); + bufferbuilder.vertex(j, this.y0, 0.0D).uv(1.0F, 0.0F).color(0, 0, 0, 255).endVertex(); + bufferbuilder.vertex(i, this.y0, 0.0D).uv(0.0F, 0.0F).color(0, 0, 0, 255).endVertex(); + tessellator.end(); + bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); + bufferbuilder.vertex(i, l1 + k1, 0.0D).uv(0.0F, 1.0F).color(128, 128, 128, 255).endVertex(); + bufferbuilder.vertex(j, l1 + k1, 0.0D).uv(1.0F, 1.0F).color(128, 128, 128, 255).endVertex(); + bufferbuilder.vertex(j, l1, 0.0D).uv(1.0F, 0.0F).color(128, 128, 128, 255).endVertex(); + bufferbuilder.vertex(i, l1, 0.0D).uv(0.0F, 0.0F).color(128, 128, 128, 255).endVertex(); + tessellator.end(); + bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); + bufferbuilder.vertex(i, l1 + k1 - 1, 0.0D).uv(0.0F, 1.0F).color(192, 192, 192, 255).endVertex(); + bufferbuilder.vertex(j - 1, l1 + k1 - 1, 0.0D).uv(1.0F, 1.0F).color(192, 192, 192, 255).endVertex(); + bufferbuilder.vertex(j - 1, l1, 0.0D).uv(1.0F, 0.0F).color(192, 192, 192, 255).endVertex(); + bufferbuilder.vertex(i, l1, 0.0D).uv(0.0F, 0.0F).color(192, 192, 192, 255).endVertex(); + tessellator.end(); + } + +// this.renderDecorations(p_render_1_, p_render_2_); + RenderSystem.enableTexture(); + RenderSystem.disableBlend(); + } + + public int getMaxScroll() { + return Math.max(0, this.getMaxPosition() - (this.y1 - this.y0 - 4)); + } + + @OnlyIn(Dist.CLIENT) + public class ShaderTypeEntry extends ObjectSelectionList.Entry { + private final ShaderType shaderType; + + public ShaderTypeEntry(ShaderType shaderType) { + this.shaderType = shaderType; + } + + @Override + public void render(PoseStack ms, int itemIndex, int rowTop, int rowLeft, int rowWidth, int rowHeight, int mouseX, int mouseY, boolean hovered, float partialTicks) { + if (rowTop + 10 > ShaderTypeList.this.y0 && rowTop + rowHeight - 5 < ShaderTypeList.this.y1) + drawString(ms, font, shaderType.name, ShaderTypeList.this.x0 + 8, rowTop + 4, 0xFFFFFF); + } + + @Override + public boolean mouseClicked(double p_mouseClicked_1_, double p_mouseClicked_3_, int p_mouseClicked_5_) { + if (p_mouseClicked_5_ == 0) { + setSelected(this); + return true; + } else { + return false; + } + } + + @Override + public Component getNarration() { + return null; + } + } + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java b/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java index 8b5a594..d150e44 100644 --- a/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java +++ b/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java @@ -1,484 +1,517 @@ package nl.requios.effortlessbuilding.gui.buildmode; -import java.util.ArrayList; -import java.util.concurrent.TimeUnit; - -import net.minecraft.client.resources.I18n; -import net.minecraft.client.settings.KeyBinding; -import net.minecraft.util.text.TextFormatting; +import com.mojang.blaze3d.vertex.*; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.math.Vector4f; +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.resources.language.I18n; +import net.minecraft.client.KeyMapping; +import net.minecraft.client.resources.sounds.SimpleSoundInstance; +import net.minecraft.core.Direction; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmode.ModeOptions; import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; +import nl.requios.effortlessbuilding.network.ModeActionMessage; +import nl.requios.effortlessbuilding.network.ModeSettingsMessage; +import nl.requios.effortlessbuilding.network.PacketHandler; import nl.requios.effortlessbuilding.proxy.ClientProxy; import org.apache.commons.lang3.text.WordUtils; import org.lwjgl.opengl.GL11; -import com.google.common.base.Stopwatch; +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.ArrayList; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.util.EnumFacing; -import net.minecraftforge.client.model.ModelLoader; +import static nl.requios.effortlessbuilding.buildmode.ModeOptions.*; -import static nl.requios.effortlessbuilding.buildmode.BuildModes.*; +import nl.requios.effortlessbuilding.buildmode.BuildModes.BuildModeEnum; +import nl.requios.effortlessbuilding.buildmode.ModeOptions.ActionEnum; +import nl.requios.effortlessbuilding.buildmode.ModeOptions.OptionEnum; /** - * From Chisels and Bits by AlgorithmX2 + * Initially from Chisels and Bits by AlgorithmX2 * https://github.com/AlgorithmX2/Chisels-and-Bits/blob/1.12/src/main/java/mod/chiselsandbits/client/gui/ChiselsAndBitsMenu.java */ -public class RadialMenu extends GuiScreen { - private final float TIME_SCALE = 0.01f; - public static final RadialMenu instance = new RadialMenu(); +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class RadialMenu extends Screen { - private float visibility = 0.0f; - private Stopwatch lastChange = Stopwatch.createStarted(); - public BuildModeEnum switchTo = null; - public ModeOptions.ActionEnum doAction = null; - public boolean actionUsed = false; + private final Vector4f radialButtonColor = new Vector4f(0f, 0f, 0f, .5f); + private final Vector4f sideButtonColor = new Vector4f(.5f, .5f, .5f, .5f); + private final Vector4f highlightColor = new Vector4f(.6f, .8f, 1f, .6f); + private final Vector4f selectedColor = new Vector4f(0f, .5f, 1f, .5f); + private final Vector4f highlightSelectedColor = new Vector4f(0.2f, .7f, 1f, .7f); - private float clampVis(final float f) { - return Math.max( 0.0f, Math.min( 1.0f, f ) ); - } + private final int whiteTextColor = 0xffffffff; + private final int watermarkTextColor = 0x88888888; + private final int descriptionTextColor = 0xdd888888; + private final int optionTextColor = 0xeeeeeeff; + + private final double ringInnerEdge = 40; + private final double ringOuterEdge = 80; + private final double categoryLineOuterEdge = 42; + private final double textDistance = 90; + private final double buttonDistance = 120; + private final float fadeSpeed = 0.3f; + private final int descriptionHeight = 100; + + public static final RadialMenu instance = new RadialMenu(); + public BuildModeEnum switchTo = null; + public ActionEnum doAction = null; + public boolean performedActionUsingMouse; + + private float visibility; + + public RadialMenu() { + super(new TranslatableComponent("effortlessbuilding.screen.radial_menu")); + } - public void raiseVisibility() { - visibility = clampVis( visibility + lastChange.elapsed( TimeUnit.MILLISECONDS ) * TIME_SCALE ); - lastChange = Stopwatch.createStarted(); - } + public boolean isVisible() { + return Minecraft.getInstance().screen instanceof RadialMenu; + } - public void decreaseVisibility() { - visibility = clampVis( visibility - lastChange.elapsed( TimeUnit.MILLISECONDS ) * TIME_SCALE ); - lastChange = Stopwatch.createStarted(); - } + @Override + protected void init() { + super.init(); + performedActionUsingMouse = false; + visibility = 0f; + } + + @Override + public void tick() { + super.tick(); + + if (!ClientProxy.isKeybindDown(2)) { + onClose(); + } + } + + @Override + public void render(PoseStack ms, final int mouseX, final int mouseY, final float partialTicks) { + BuildModeEnum currentBuildMode = ModeSettingsManager.getModeSettings(minecraft.player).getBuildMode(); + + ms.pushPose(); + ms.translate(0, 0, 200); + + visibility += fadeSpeed * partialTicks; + if (visibility > 1f) visibility = 1f; + + final int startColor = (int) (visibility * 98) << 24; + final int endColor = (int) (visibility * 128) << 24; + + fillGradient(ms, 0, 0, width, height, startColor, endColor); + + RenderSystem.disableTexture(); + RenderSystem.enableBlend(); + RenderSystem.blendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0); + final Tesselator tessellator = Tesselator.getInstance(); + final BufferBuilder buffer = tessellator.getBuilder(); + + buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); + + final double middleX = width / 2.0; + final double middleY = height / 2.0; + + //Fix for high def (retina) displays: use custom mouse coordinates + //Borrowed from GameRenderer::updateCameraAndRender + int mouseXX = (int) (minecraft.mouseHandler.xpos() * (double) minecraft.getWindow().getGuiScaledWidth() / (double) minecraft.getWindow().getScreenWidth()); + int mouseYY = (int) (minecraft.mouseHandler.ypos() * (double) minecraft.getWindow().getGuiScaledHeight() / (double) minecraft.getWindow().getScreenHeight()); + + final double mouseXCenter = mouseXX - middleX; + final double mouseYCenter = mouseYY - middleY; + double mouseRadians = Math.atan2(mouseYCenter, mouseXCenter); + + final double quarterCircle = Math.PI / 2.0; + + if (mouseRadians < -quarterCircle) { + mouseRadians = mouseRadians + Math.PI * 2; + } + + final ArrayList modes = new ArrayList(); + final ArrayList buttons = new ArrayList(); + + //Add build modes + for (final BuildModeEnum mode : BuildModeEnum.values()) { + modes.add(new MenuRegion(mode)); + } + + //Add actions + buttons.add(new MenuButton(ActionEnum.UNDO.name, ActionEnum.UNDO, -buttonDistance - 26, -13, Direction.UP)); + buttons.add(new MenuButton(ActionEnum.REDO.name, ActionEnum.REDO, -buttonDistance, -13, Direction.UP)); +// buttons.add(new MenuButton(ActionEnum.OPEN_PLAYER_SETTINGS.name, ActionEnum.OPEN_PLAYER_SETTINGS, -buttonDistance - 26 - 13, 13, Direction.DOWN)); + buttons.add(new MenuButton(ActionEnum.OPEN_MODIFIER_SETTINGS.name, ActionEnum.OPEN_MODIFIER_SETTINGS, -buttonDistance - 26, 13, Direction.DOWN)); + buttons.add(new MenuButton(ActionEnum.REPLACE.name, ActionEnum.REPLACE, -buttonDistance, 13, Direction.DOWN)); + + //Add buildmode dependent options + OptionEnum[] options = currentBuildMode.options; + for (int i = 0; i < options.length; i++) { + for (int j = 0; j < options[i].actions.length; j++) { + ActionEnum action = options[i].actions[j]; + buttons.add(new MenuButton(action.name, action, buttonDistance + j * 26, -13 + i * 39, Direction.DOWN)); + } + } + + switchTo = null; + doAction = null; + + //Draw buildmode backgrounds + drawRadialButtonBackgrounds(currentBuildMode, buffer, middleX, middleY, mouseXCenter, mouseYCenter, mouseRadians, ringInnerEdge, ringOuterEdge, quarterCircle, modes); + + //Draw action backgrounds + drawSideButtonBackgrounds(buffer, middleX, middleY, mouseXCenter, mouseYCenter, buttons); + + tessellator.end(); + RenderSystem.disableBlend(); + RenderSystem.enableTexture(); + + drawIcons(ms, tessellator, buffer, middleX, middleY, ringInnerEdge, ringOuterEdge, modes, buttons); + + drawTexts(ms, currentBuildMode, middleX, middleY, textDistance, buttonDistance, modes, buttons, options); + + ms.popPose(); + } + + private void drawRadialButtonBackgrounds(BuildModeEnum currentBuildMode, BufferBuilder buffer, double middleX, double middleY, double mouseXCenter, double mouseYCenter, double mouseRadians, double ringInnerEdge, double ringOuterEdge, double quarterCircle, ArrayList modes) { + if (!modes.isEmpty()) { + final int totalModes = Math.max(3, modes.size()); + final double fragment = Math.PI * 0.005; //gap between buttons in radians at inner edge + final double fragment2 = Math.PI * 0.0025; //gap between buttons in radians at outer edge + final double radiansPerObject = 2.0 * Math.PI / totalModes; + + for (int i = 0; i < modes.size(); i++) { + MenuRegion menuRegion = modes.get(i); + final double beginRadians = i * radiansPerObject - quarterCircle; + final double endRadians = (i + 1) * radiansPerObject - quarterCircle; + + menuRegion.x1 = Math.cos(beginRadians); + menuRegion.x2 = Math.cos(endRadians); + menuRegion.y1 = Math.sin(beginRadians); + menuRegion.y2 = Math.sin(endRadians); + + final double x1m1 = Math.cos(beginRadians + fragment) * ringInnerEdge; + final double x2m1 = Math.cos(endRadians - fragment) * ringInnerEdge; + final double y1m1 = Math.sin(beginRadians + fragment) * ringInnerEdge; + final double y2m1 = Math.sin(endRadians - fragment) * ringInnerEdge; + + final double x1m2 = Math.cos(beginRadians + fragment2) * ringOuterEdge; + final double x2m2 = Math.cos(endRadians - fragment2) * ringOuterEdge; + final double y1m2 = Math.sin(beginRadians + fragment2) * ringOuterEdge; + final double y2m2 = Math.sin(endRadians - fragment2) * ringOuterEdge; + + final boolean isSelected = currentBuildMode.ordinal() == i; + final boolean isMouseInQuad = inTriangle(x1m1, y1m1, x2m2, y2m2, x2m1, y2m1, mouseXCenter, mouseYCenter) + || inTriangle(x1m1, y1m1, x1m2, y1m2, x2m2, y2m2, mouseXCenter, mouseYCenter); + final boolean isHighlighted = beginRadians <= mouseRadians && mouseRadians <= endRadians && isMouseInQuad; + + Vector4f color = radialButtonColor; + if (isSelected) color = selectedColor; + if (isHighlighted) color = highlightColor; + if (isSelected && isHighlighted) color = highlightSelectedColor; + + if (isHighlighted) { + menuRegion.highlighted = true; + switchTo = menuRegion.mode; + } + + buffer.vertex(middleX + x1m1, middleY + y1m1, getBlitOffset()).color(color.x(), color.y(), color.z(), color.w()).endVertex(); + buffer.vertex(middleX + x2m1, middleY + y2m1, getBlitOffset()).color(color.x(), color.y(), color.z(), color.w()).endVertex(); + buffer.vertex(middleX + x2m2, middleY + y2m2, getBlitOffset()).color(color.x(), color.y(), color.z(), color.w()).endVertex(); + buffer.vertex(middleX + x1m2, middleY + y1m2, getBlitOffset()).color(color.x(), color.y(), color.z(), color.w()).endVertex(); + + //Category line + color = menuRegion.mode.category.color; + + final double x1m3 = Math.cos(beginRadians + fragment) * categoryLineOuterEdge; + final double x2m3 = Math.cos(endRadians - fragment) * categoryLineOuterEdge; + final double y1m3 = Math.sin(beginRadians + fragment) * categoryLineOuterEdge; + final double y2m3 = Math.sin(endRadians - fragment) * categoryLineOuterEdge; + + buffer.vertex(middleX + x1m1, middleY + y1m1, getBlitOffset()).color(color.x(), color.y(), color.z(), color.w()).endVertex(); + buffer.vertex(middleX + x2m1, middleY + y2m1, getBlitOffset()).color(color.x(), color.y(), color.z(), color.w()).endVertex(); + buffer.vertex(middleX + x2m3, middleY + y2m3, getBlitOffset()).color(color.x(), color.y(), color.z(), color.w()).endVertex(); + buffer.vertex(middleX + x1m3, middleY + y1m3, getBlitOffset()).color(color.x(), color.y(), color.z(), color.w()).endVertex(); + } + } + } + + private void drawSideButtonBackgrounds(BufferBuilder buffer, double middleX, double middleY, double mouseXCenter, double mouseYCenter, ArrayList buttons) { + for (final MenuButton btn : buttons) { + + final boolean isSelected = + btn.action == getBuildSpeed() || + btn.action == getFill() || + btn.action == getCubeFill() || + btn.action == getRaisedEdge() || + btn.action == getLineThickness() || + btn.action == getCircleStart(); + + final boolean isHighlighted = btn.x1 <= mouseXCenter && btn.x2 >= mouseXCenter && btn.y1 <= mouseYCenter && btn.y2 >= mouseYCenter; + + Vector4f color = sideButtonColor; + if (isSelected) color = selectedColor; + if (isHighlighted) color = highlightColor; + if (isSelected && isHighlighted) color = highlightSelectedColor; + + if (isHighlighted) { + btn.highlighted = true; + doAction = btn.action; + } + + buffer.vertex(middleX + btn.x1, middleY + btn.y1, getBlitOffset()).color(color.x(), color.y(), color.z(), color.w()).endVertex(); + buffer.vertex(middleX + btn.x1, middleY + btn.y2, getBlitOffset()).color(color.x(), color.y(), color.z(), color.w()).endVertex(); + buffer.vertex(middleX + btn.x2, middleY + btn.y2, getBlitOffset()).color(color.x(), color.y(), color.z(), color.w()).endVertex(); + buffer.vertex(middleX + btn.x2, middleY + btn.y1, getBlitOffset()).color(color.x(), color.y(), color.z(), color.w()).endVertex(); + } + } + + private void drawIcons(PoseStack ms, Tesselator tessellator, BufferBuilder buffer, double middleX, double middleY, double ringInnerEdge, double ringOuterEdge, ArrayList modes, ArrayList buttons) { + ms.pushPose(); + RenderSystem.enableTexture(); + RenderSystem.setShader(GameRenderer::getPositionColorTexShader); + RenderSystem.setShaderColor(1f, 1f, 1f, 1f); + + //Draw buildmode icons + for (final MenuRegion menuRegion : modes) { + + final double x = (menuRegion.x1 + menuRegion.x2) * 0.5 * (ringOuterEdge * 0.55 + 0.45 * ringInnerEdge); + final double y = (menuRegion.y1 + menuRegion.y2) * 0.5 * (ringOuterEdge * 0.55 + 0.45 * ringInnerEdge); + + RenderSystem.setShaderTexture(0, new ResourceLocation(EffortlessBuilding.MODID, "textures/icons/" + menuRegion.mode.name().toLowerCase() + ".png")); + blit(ms, (int) (middleX + x - 8), (int) (middleY + y - 8), 16, 16, 0, 0, 18, 18, 18, 18); + } + + //Draw action icons + for (final MenuButton button : buttons) { + + final double x = (button.x1 + button.x2) / 2 + 0.01; + final double y = (button.y1 + button.y2) / 2 + 0.01; + + RenderSystem.setShaderTexture(0, new ResourceLocation(EffortlessBuilding.MODID, "textures/icons/" + button.action.name().toLowerCase() + ".png")); + blit(ms, (int) (middleX + x - 8), (int) (middleY + y - 8), 16, 16, 0, 0, 18, 18, 18, 18); + } + + ms.popPose(); + } + + private void drawTexts(PoseStack ms, BuildModeEnum currentBuildMode, double middleX, double middleY, double textDistance, double buttonDistance, ArrayList modes, ArrayList buttons, OptionEnum[] options) { + //font.drawStringWithShadow("Actions", (int) (middleX - buttonDistance - 13) - font.getStringWidth("Actions") * 0.5f, (int) middleY - 38, 0xffffffff); + + //Draw option strings + for (int i = 0; i < currentBuildMode.options.length; i++) { + OptionEnum option = options[i]; + font.drawShadow(ms, I18n.get(option.name), (int) (middleX + buttonDistance - 9), (int) middleY - 37 + i * 39, optionTextColor); + } + + String credits = "Effortless Building"; + font.drawShadow(ms, credits, width - font.width(credits) - 4, height - 10, watermarkTextColor); + + //Draw buildmode text + for (final MenuRegion menuRegion : modes) { + + if (menuRegion.highlighted) { + final double x = (menuRegion.x1 + menuRegion.x2) * 0.5; + final double y = (menuRegion.y1 + menuRegion.y2) * 0.5; + + int fixed_x = (int) (x * textDistance); + int fixed_y = (int) (y * textDistance) - font.lineHeight / 2; + String text = I18n.get(menuRegion.mode.getNameKey()); + + if (x <= -0.2) { + fixed_x -= font.width(text); + } else if (-0.2 <= x && x <= 0.2) { + fixed_x -= font.width(text) / 2; + } + + font.drawShadow(ms, text, (int) middleX + fixed_x, (int) middleY + fixed_y, whiteTextColor); + + //Draw description + text = I18n.get(menuRegion.mode.getDescriptionKey()); + font.drawShadow(ms, text, (int) middleX - font.width(text) / 2f, (int) middleY + descriptionHeight, descriptionTextColor); + } + } + + //Draw action text + for (final MenuButton button : buttons) { + if (button.highlighted) { + String text = ChatFormatting.AQUA + button.name; + + //Add keybind in brackets + String keybind = findKeybind(button, currentBuildMode); + String keybindFormatted = ""; + if (!keybind.isEmpty()) + keybindFormatted = ChatFormatting.GRAY + "(" + WordUtils.capitalizeFully(keybind) + ")"; + + if (button.textSide == Direction.WEST) { + + font.draw(ms, text, (int) (middleX + button.x1 - 8) - font.width(text), + (int) (middleY + button.y1 + 6), whiteTextColor); + + } else if (button.textSide == Direction.EAST) { + + font.draw(ms, text, (int) (middleX + button.x2 + 8), + (int) (middleY + button.y1 + 6), whiteTextColor); + + } else if (button.textSide == Direction.UP || button.textSide == Direction.NORTH) { + + font.draw(ms, keybindFormatted, (int) (middleX + (button.x1 + button.x2) * 0.5 - font.width(keybindFormatted) * 0.5), + (int) (middleY + button.y1 - 26), whiteTextColor); + + font.draw(ms, text, (int) (middleX + (button.x1 + button.x2) * 0.5 - font.width(text) * 0.5), + (int) (middleY + button.y1 - 14), whiteTextColor); + + } else if (button.textSide == Direction.DOWN || button.textSide == Direction.SOUTH) { + + font.draw(ms, text, (int) (middleX + (button.x1 + button.x2) * 0.5 - font.width(text) * 0.5), + (int) (middleY + button.y1 + 26), whiteTextColor); + + font.draw(ms, keybindFormatted, (int) (middleX + (button.x1 + button.x2) * 0.5 - font.width(keybindFormatted) * 0.5), + (int) (middleY + button.y1 + 38), whiteTextColor); + + } + + } + } + } + + private String findKeybind(MenuButton button, BuildModeEnum currentBuildMode){ + String result = ""; + int keybindingIndex = -1; + if (button.action == ActionEnum.UNDO) keybindingIndex = 3; + if (button.action == ActionEnum.REDO) keybindingIndex = 4; + if (button.action == ActionEnum.REPLACE) keybindingIndex = 1; + if (button.action == ActionEnum.OPEN_MODIFIER_SETTINGS) keybindingIndex = 0; + + if (keybindingIndex != -1) { + KeyMapping keyMap = ClientProxy.keyBindings[keybindingIndex]; + + if (!keyMap.getKeyModifier().name().equals("none")) { + result = keyMap.getKeyModifier().name() + " "; + } + result += I18n.get(keyMap.getKey().getName()); + } + + if (currentBuildMode.options.length > 0) { + //Add (ctrl) to first two actions of first option + if (button.action == currentBuildMode.options[0].actions[0] + || button.action == currentBuildMode.options[0].actions[1]) { + result = I18n.get(ClientProxy.keyBindings[5].getKey().getName()); + if (result.equals("Left Control")) result = "Ctrl"; + } + } + return result; + } + + private boolean inTriangle(final double x1, final double y1, final double x2, final double y2, + final double x3, final double y3, final double x, final double y) { + final double ab = (x1 - x) * (y2 - y) - (x2 - x) * (y1 - y); + final double bc = (x2 - x) * (y3 - y) - (x3 - x) * (y2 - y); + final double ca = (x3 - x) * (y1 - y) - (x1 - x) * (y3 - y); + return sign(ab) == sign(bc) && sign(bc) == sign(ca); + } + + private int sign(final double n) { + return n > 0 ? 1 : -1; + } + + @Override + public boolean isPauseScreen() { + return false; + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) { + performAction(true); + + return super.mouseClicked(mouseX, mouseY, mouseButton); + } + + @Override + public void onClose() { + super.onClose(); + //After onClose so it can open another screen + if (!performedActionUsingMouse) performAction(false); + } + + private void performAction(boolean fromMouseClick) { + LocalPlayer player = Minecraft.getInstance().player; + + ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); + + if (switchTo != null) { + playRadialMenuSound(); + + modeSettings.setBuildMode(switchTo); + ModeSettingsManager.setModeSettings(player, modeSettings); + PacketHandler.INSTANCE.sendToServer(new ModeSettingsMessage(modeSettings)); + + EffortlessBuilding.log(player, I18n.get(modeSettings.getBuildMode().getNameKey()), true); + + if (fromMouseClick) performedActionUsingMouse = true; + } + + //Perform button action + ModeOptions.ActionEnum action = doAction; + if (action != null) { + playRadialMenuSound(); + + ModeOptions.performAction(player, action); + PacketHandler.INSTANCE.sendToServer(new ModeActionMessage(action)); + + if (fromMouseClick) performedActionUsingMouse = true; + } + } + + public static void playRadialMenuSound() { + final float volume = 0.1f; + if (volume >= 0.0001f) { + SimpleSoundInstance sound = new SimpleSoundInstance(SoundEvents.UI_BUTTON_CLICK, SoundSource.MASTER, volume, 1.0f, Minecraft.getInstance().player.blockPosition()); + Minecraft.getInstance().getSoundManager().play(sound); + } + } + + private static class MenuButton { + + public final ActionEnum action; + public double x1, x2; + public double y1, y2; + public boolean highlighted; + public String name; + public Direction textSide; + + public MenuButton(final String name, final ActionEnum action, final double x, final double y, + final Direction textSide) { + this.name = I18n.get(name); + this.action = action; + x1 = x - 10; + x2 = x + 10; + y1 = y - 10; + y2 = y + 10; + this.textSide = textSide; + } + + } + + static class MenuRegion { + + public final BuildModeEnum mode; + public double x1, x2; + public double y1, y2; + public boolean highlighted; + + public MenuRegion(final BuildModeEnum mode) { + this.mode = mode; + } + + } - public void setVisibility(float visibility) { - this.visibility = visibility; - } - - public boolean isVisible() { - return visibility > 0.001; - } - - public void configure(final int scaledWidth, final int scaledHeight ) { - mc = Minecraft.getMinecraft(); - fontRenderer = mc.fontRenderer; - width = scaledWidth; - height = scaledHeight; - } - - private static class MenuButton { - - public double x1, x2; - public double y1, y2; - public boolean highlighted; - - public final ModeOptions.ActionEnum action; - public String name; - public EnumFacing textSide; - - public MenuButton(final String name, final ModeOptions.ActionEnum action, final double x, final double y, - final EnumFacing textSide) { - this.name = I18n.format(name); - this.action = action; - x1 = x - 10; - x2 = x + 10; - y1 = y - 10; - y2 = y + 10; - this.textSide = textSide; - } - - } - - static class MenuRegion { - - public final BuildModeEnum mode; - public double x1, x2; - public double y1, y2; - public boolean highlighted; - - public MenuRegion(final BuildModeEnum mode) { - this.mode = mode; - } - - } - - @Override - public void drawScreen(final int mouseX, final int mouseY, final float partialTicks) { - if (!isVisible()) return; - - BuildModeEnum currentBuildMode = ModeSettingsManager.getModeSettings(Minecraft.getMinecraft().player).getBuildMode(); - - GlStateManager.pushMatrix(); - GlStateManager.translate( 0.0F, 0.0F, 200.0F ); - - final int startColor = (int) ( visibility * 98 ) << 24; - final int endColor = (int) ( visibility * 128 ) << 24; - - drawGradientRect(0, 0, width, height, startColor, endColor); - - GlStateManager.disableTexture2D(); - GlStateManager.enableBlend(); - GlStateManager.disableAlpha(); - GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0); - GlStateManager.shadeModel(GL11.GL_SMOOTH); - final Tessellator tessellator = Tessellator.getInstance(); - final BufferBuilder buffer = tessellator.getBuffer(); - - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR); - - final double middleX = width / 2.0; - final double middleY = height / 2.0; - - final double mouseXCenter = mouseX - middleX; - final double mouseYCenter = mouseY - middleY; - double mouseRadians = Math.atan2(mouseYCenter, mouseXCenter); - - final double ringInnerEdge = 20; - final double ringOuterEdge = 50; - final double textDistance = 60; - final double buttonDistance = 90; - final double quarterCircle = Math.PI / 2.0; - - if ( mouseRadians < -quarterCircle ) { - mouseRadians = mouseRadians + Math.PI * 2; - } - - final ArrayList modes = new ArrayList(); - final ArrayList buttons = new ArrayList(); - - //Add build modes - for (final BuildModeEnum mode : BuildModeEnum.values()) { - modes.add(new MenuRegion(mode)); - } - - //Add actions - buttons.add(new MenuButton(ModeOptions.ActionEnum.UNDO.name, ModeOptions.ActionEnum.UNDO, -buttonDistance - 26, -13, EnumFacing.UP)); - buttons.add(new MenuButton(ModeOptions.ActionEnum.REDO.name, ModeOptions.ActionEnum.REDO, -buttonDistance, -13, EnumFacing.UP)); - buttons.add(new MenuButton(ModeOptions.ActionEnum.OPEN_MODIFIER_SETTINGS.name, ModeOptions.ActionEnum.OPEN_MODIFIER_SETTINGS, -buttonDistance - 26, 13, EnumFacing.DOWN)); - buttons.add(new MenuButton(ModeOptions.ActionEnum.REPLACE.name, ModeOptions.ActionEnum.REPLACE, -buttonDistance, 13, EnumFacing.DOWN)); - - //Add buildmode dependent options - ModeOptions.ActionEnum[] options = currentBuildMode.options; - for (int i = 0; i < options.length; i++) { - ModeOptions.ActionEnum action = options[i]; - buttons.add(new MenuButton(action.name, action, buttonDistance + i * 26, -13, EnumFacing.DOWN)); - } - - switchTo = null; - doAction = null; - - //Draw buildmode backgrounds - if (!modes.isEmpty()) { - final int totalModes = Math.max( 3, modes.size() ); - int currentMode = 0; - final double fragment = Math.PI * 0.005; - final double fragment2 = Math.PI * 0.0025; - final double perObject = 2.0 * Math.PI / totalModes; - - for (int i = 0; i < modes.size(); i++) { - MenuRegion menuRegion = modes.get(i); - final double beginRadians = currentMode * perObject - quarterCircle; - final double endRadians = (currentMode + 1) * perObject - quarterCircle; - - menuRegion.x1 = Math.cos(beginRadians); - menuRegion.x2 = Math.cos(endRadians); - menuRegion.y1 = Math.sin(beginRadians); - menuRegion.y2 = Math.sin(endRadians); - - final double x1m1 = Math.cos(beginRadians + fragment) * ringInnerEdge; - final double x2m1 = Math.cos(endRadians - fragment) * ringInnerEdge; - final double y1m1 = Math.sin(beginRadians + fragment) * ringInnerEdge; - final double y2m1 = Math.sin(endRadians - fragment) * ringInnerEdge; - - final double x1m2 = Math.cos(beginRadians + fragment2) * ringOuterEdge; - final double x2m2 = Math.cos(endRadians - fragment2) * ringOuterEdge; - final double y1m2 = Math.sin(beginRadians + fragment2) * ringOuterEdge; - final double y2m2 = Math.sin(endRadians - fragment2) * ringOuterEdge; - - float r = 0.0f; - float g = 0.0f; - float b = 0.0f; - float a = 0.5f; - - //check if current mode - int buildMode = currentBuildMode.ordinal(); - if (buildMode == i) { - r = 0f; - g = 0.5f; - b = 1f; - a = 0.5f; - //menuRegion.highlighted = true; //draw text - } - - //check if mouse is over this region - final boolean isMouseInQuad = inTriangle(x1m1, y1m1, x2m2, y2m2, x2m1, y2m1, mouseXCenter, mouseYCenter) - || inTriangle(x1m1, y1m1, x1m2, y1m2, x2m2, y2m2, mouseXCenter, mouseYCenter); - - if (beginRadians <= mouseRadians && mouseRadians <= endRadians && isMouseInQuad) { - r = 0.6f; - g = 0.8f; - b = 1f; - a = 0.6f; - menuRegion.highlighted = true; - switchTo = menuRegion.mode; - } - - buffer.pos(middleX + x1m1, middleY + y1m1, zLevel).color(r, g, b, a).endVertex(); - buffer.pos(middleX + x2m1, middleY + y2m1, zLevel).color(r, g, b, a).endVertex(); - buffer.pos(middleX + x2m2, middleY + y2m2, zLevel).color(r, g, b, a).endVertex(); - buffer.pos(middleX + x1m2, middleY + y1m2, zLevel).color(r, g, b, a).endVertex(); - - currentMode++; - } - } - - //Draw action backgrounds - for (final MenuButton btn : buttons) { - float r = 0.5f; - float g = 0.5f; - float b = 0.5f; - float a = 0.5f; - - //highlight when active option - if (btn.action == ModeOptions.getBuildSpeed() || - btn.action == ModeOptions.getFill() || - btn.action == ModeOptions.getCubeFill() || - btn.action == ModeOptions.getRaisedEdge() || - btn.action == ModeOptions.getLineThickness()) { - r = 0.0f; - g = 0.5f; - b = 1f; - a = 0.6f; - } - - //highlight when mouse over - if (btn.x1 <= mouseXCenter && btn.x2 >= mouseXCenter && btn.y1 <= mouseYCenter && btn.y2 >= mouseYCenter) { - r = 0.6f; - g = 0.8f; - b = 1f; - a = 0.6f; - btn.highlighted = true; - doAction = btn.action; - } - - buffer.pos(middleX + btn.x1, middleY + btn.y1, zLevel).color(r, g, b, a).endVertex(); - buffer.pos(middleX + btn.x1, middleY + btn.y2, zLevel).color(r, g, b, a).endVertex(); - buffer.pos(middleX + btn.x2, middleY + btn.y2, zLevel).color(r, g, b, a).endVertex(); - buffer.pos(middleX + btn.x2, middleY + btn.y1, zLevel).color(r, g, b, a).endVertex(); - } - - tessellator.draw(); - - GlStateManager.shadeModel(GL11.GL_FLAT); - - GlStateManager.translate(0f, 0f, 5f); - GlStateManager.enableTexture2D(); - GlStateManager.color(1f, 1f, 1f, 1f); - GlStateManager.disableBlend(); - GlStateManager.enableAlpha(); - GlStateManager.bindTexture(Minecraft.getMinecraft().getTextureMapBlocks().getGlTextureId()); - - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR); - - //Draw buildmode icons - for (final MenuRegion menuRegion : modes) { - - final double x = (menuRegion.x1 + menuRegion.x2) * 0.5 * (ringOuterEdge * 0.6 + 0.4 * ringInnerEdge); - final double y = (menuRegion.y1 + menuRegion.y2) * 0.5 * (ringOuterEdge * 0.6 + 0.4 * ringInnerEdge); - - final TextureAtlasSprite sprite = ClientProxy.getBuildModeIcon(menuRegion.mode); - - final double x1 = x - 8; - final double x2 = x + 8; - final double y1 = y - 8; - final double y2 = y + 8; - - final float f = 1f; - final float a = 1f; - - final double u1 = 0; - final double u2 = 16; - final double v1 = 0; - final double v2 = 16; - - buffer.pos(middleX + x1, middleY + y1, zLevel).tex(sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v1)).color(f, f, f, a).endVertex(); - buffer.pos(middleX + x1, middleY + y2, zLevel).tex(sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v2)).color(f, f, f, a).endVertex(); - buffer.pos(middleX + x2, middleY + y2, zLevel).tex(sprite.getInterpolatedU(u2), sprite.getInterpolatedV(v2)).color(f, f, f, a).endVertex(); - buffer.pos(middleX + x2, middleY + y1, zLevel).tex(sprite.getInterpolatedU(u2), sprite.getInterpolatedV(v1)).color(f, f, f, a).endVertex(); - } - - //Draw action icons - for (final MenuButton button : buttons) { - - final float f = 1f; - final float a = 1f; - - final double u1 = 0; - final double u2 = 16; - final double v1 = 0; - final double v2 = 16; - - final TextureAtlasSprite sprite = ClientProxy.getModeOptionIcon(button.action); - - final double btnmiddleX = (button.x1 + button.x2) / 2 + 0.01; - final double btnmiddleY = (button.y1 + button.y2) / 2 + 0.01; - final double btnx1 = btnmiddleX - 8; - final double btnx2 = btnmiddleX + 8; - final double btny1 = btnmiddleY - 8; - final double btny2 = btnmiddleY + 8; - - buffer.pos(middleX + btnx1, middleY + btny1, zLevel).tex(sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v1)).color(f, f, f, a).endVertex(); - buffer.pos(middleX + btnx1, middleY + btny2, zLevel).tex(sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v2)).color(f, f, f, a).endVertex(); - buffer.pos(middleX + btnx2, middleY + btny2, zLevel).tex(sprite.getInterpolatedU(u2), sprite.getInterpolatedV(v2)).color(f, f, f, a).endVertex(); - buffer.pos(middleX + btnx2, middleY + btny1, zLevel).tex(sprite.getInterpolatedU(u2), sprite.getInterpolatedV(v1)).color(f, f, f, a).endVertex(); - } - - tessellator.draw(); - - //Draw strings - //fontRenderer.drawStringWithShadow("Actions", (int) (middleX - buttonDistance - 13) - fontRenderer.getStringWidth("Actions") * 0.5f, (int) middleY - 38, 0xffffffff); - String title = ""; - if (currentBuildMode.options.length > 0) { - switch (currentBuildMode.options[0]) { - case NORMAL_SPEED: - case FAST_SPEED: - title = I18n.format("effortlessbuilding.action.build_speed"); - break; - case FULL: - case HOLLOW: - case CUBE_FULL: - case CUBE_HOLLOW: - case CUBE_SKELETON: - title = I18n.format("effortlessbuilding.action.filling"); - break; - case SHORT_EDGE: - case LONG_EDGE: - title = I18n.format("effortlessbuilding.action.raised_edge"); - break; - case THICKNESS_1: - case THICKNESS_3: - case THICKNESS_5: - title = I18n.format("effortlessbuilding.action.thickness"); - break; - } - } - fontRenderer.drawStringWithShadow(title, (int) (middleX + buttonDistance - 9), (int) middleY - 37, 0xeeeeeeff); - - String credits = "Effortless Building"; - fontRenderer.drawStringWithShadow(credits, width - fontRenderer.getStringWidth(credits) - 4, height - 10, 0x88888888); - - //Draw buildmode text - for (final MenuRegion menuRegion : modes) { - - if (menuRegion.highlighted) { - final double x = (menuRegion.x1 + menuRegion.x2) * 0.5; - final double y = (menuRegion.y1 + menuRegion.y2) * 0.5; - - int fixed_x = (int) (x * textDistance); - final int fixed_y = (int) (y * textDistance) - fontRenderer.FONT_HEIGHT / 2; - final String text = I18n.format(menuRegion.mode.name); - - if ( x <= -0.2 ) { - fixed_x -= fontRenderer.getStringWidth(text); - } else if ( -0.2 <= x && x <= 0.2 ) { - fixed_x -= fontRenderer.getStringWidth(text) / 2; - } - - fontRenderer.drawStringWithShadow(text, (int) middleX + fixed_x, (int) middleY + fixed_y, 0xffffffff); - } - } - - //Draw action text - for (final MenuButton button : buttons) { - if (button.highlighted) { - String text = TextFormatting.AQUA + button.name; - int wrap = 120; - String keybind = ""; - String keybindFormatted = ""; - - //Add keybind in brackets - if (button.action == ModeOptions.ActionEnum.UNDO) { - keybind = ClientProxy.keyBindings[4].getDisplayName(); - } - if (button.action == ModeOptions.ActionEnum.REDO) { - keybind = ClientProxy.keyBindings[5].getDisplayName(); - } - if (button.action == ModeOptions.ActionEnum.REPLACE) { - keybind = ClientProxy.keyBindings[1].getDisplayName(); - } - if (button.action == ModeOptions.ActionEnum.OPEN_MODIFIER_SETTINGS) { - keybind = ClientProxy.keyBindings[0].getDisplayName(); - } - if (!keybind.isEmpty()) keybindFormatted = TextFormatting.GRAY + "(" + WordUtils.capitalizeFully(keybind) + ")"; - - if (button.textSide == EnumFacing.WEST) { - - fontRenderer.drawSplitString( text, (int) (middleX + button.x1 - 8 ) - fontRenderer.getStringWidth(text), - (int) (middleY + button.y1 + 6), wrap, 0xffffffff); - - } else if (button.textSide == EnumFacing.EAST) { - - fontRenderer.drawSplitString(text, (int) (middleX + button.x2 + 8), - (int) (middleY + button.y1 + 6 ), wrap, 0xffffffff); - - } else if (button.textSide == EnumFacing.UP || button.textSide == EnumFacing.NORTH) { - - fontRenderer.drawSplitString( keybindFormatted, (int) (middleX + (button.x1 + button.x2) * 0.5 - fontRenderer.getStringWidth(keybindFormatted) * 0.5), - (int) (middleY + button.y1 - 26), wrap,0xffffffff); - - fontRenderer.drawSplitString( text, (int) (middleX + (button.x1 + button.x2) * 0.5 - fontRenderer.getStringWidth(text) * 0.5), - (int) (middleY + button.y1 - 14), wrap,0xffffffff); - - } else if (button.textSide == EnumFacing.DOWN || button.textSide == EnumFacing.SOUTH) { - - fontRenderer.drawSplitString(text, (int) (middleX + (button.x1 + button.x2) * 0.5 - fontRenderer.getStringWidth(text) * 0.5), - (int) (middleY + button.y1 + 26), wrap, 0xffffffff); - - fontRenderer.drawSplitString(keybindFormatted, (int) (middleX + (button.x1 + button.x2) * 0.5 - fontRenderer.getStringWidth(keybindFormatted) * 0.5), - (int) (middleY + button.y1 + 38), wrap, 0xffffffff); - - } - - } - } - - GlStateManager.popMatrix(); - } - - private boolean inTriangle(final double x1, final double y1, final double x2, final double y2, - final double x3, final double y3, final double x, final double y ) { - final double ab = (x1 - x) * (y2 - y) - (x2 - x) * (y1 - y); - final double bc = (x2 - x) * (y3 - y) - (x3 - x) * (y2 - y); - final double ca = (x3 - x) * (y1 - y) - (x1 - x) * (y3 - y); - return sign(ab) == sign(bc) && sign(bc) == sign(ca); - } - - private int sign(final double n) { - return n > 0 ? 1 : -1; - } - - /** - * Called when the mouse is clicked. Args : mouseX, mouseY, clickedButton - */ - @Override - protected void mouseClicked(int mouseX, int mouseY, int mouseButton ) { - EffortlessBuilding.log("mouse clicked"); - -// KeyBinding.updateKeyBindState(); -// KeyBinding.setKeyBindState(ClientProxy.keyBindings[3].getKeyCode(), true); - -// if (mouseButton == 0) { -// this.mc.displayGuiScreen(null); -// -// if (this.mc.currentScreen == null) { -// this.mc.setIngameFocus(); -// } -// } - } } diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/buildmodifier/ArraySettingsGui.java b/src/main/java/nl/requios/effortlessbuilding/gui/buildmodifier/ArraySettingsGui.java index 2f0e867..d7cab6d 100644 --- a/src/main/java/nl/requios/effortlessbuilding/gui/buildmodifier/ArraySettingsGui.java +++ b/src/main/java/nl/requios/effortlessbuilding/gui/buildmodifier/ArraySettingsGui.java @@ -1,214 +1,202 @@ package nl.requios.effortlessbuilding.gui.buildmodifier; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.text.TextFormatting; -import net.minecraftforge.fml.client.config.GuiCheckBox; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.components.Widget; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.ChatFormatting; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmodifier.Array; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; +import nl.requios.effortlessbuilding.gui.elements.GuiCheckBoxFixed; import nl.requios.effortlessbuilding.gui.elements.GuiCollapsibleScrollEntry; import nl.requios.effortlessbuilding.gui.elements.GuiNumberField; import nl.requios.effortlessbuilding.gui.elements.GuiScrollPane; import nl.requios.effortlessbuilding.helper.ReachHelper; -import java.io.IOException; import java.util.ArrayList; import java.util.List; +@OnlyIn(Dist.CLIENT) public class ArraySettingsGui extends GuiCollapsibleScrollEntry { - protected List arrayNumberFieldList = new ArrayList<>(); + protected List arrayNumberFieldList = new ArrayList<>(); - private GuiCheckBox buttonArrayEnabled; - private GuiNumberField textArrayOffsetX, textArrayOffsetY, textArrayOffsetZ, textArrayCount; + private GuiCheckBoxFixed buttonArrayEnabled; + private GuiNumberField textArrayOffsetX, textArrayOffsetY, textArrayOffsetZ, textArrayCount; - public ArraySettingsGui(GuiScrollPane scrollPane) { - super(scrollPane); - } + public ArraySettingsGui(GuiScrollPane scrollPane) { + super(scrollPane); + } - @Override - public int initGui(int id, List buttonList) { - id = super.initGui(id, buttonList); + @Override + public void init(List renderables) { + super.init(renderables); - int y = top; - buttonArrayEnabled = new GuiCheckBox(id++, left - 15 + 8, y, "", false); - buttonList.add(buttonArrayEnabled); + int y = top; + buttonArrayEnabled = new GuiCheckBoxFixed(left - 15 + 8, y, "", false) { + @Override + public void onClick(double mouseX, double mouseY) { + super.onClick(mouseX, mouseY); + setCollapsed(!buttonArrayEnabled.isChecked()); + } + }; + renderables.add(buttonArrayEnabled); - y = top + 20; - textArrayOffsetX = new GuiNumberField(id++, id++, id++, fontRenderer, buttonList, left + 70, y, 50, 18); - textArrayOffsetX.setNumber(0); - textArrayOffsetX.setTooltip("How much each copy is shifted."); - arrayNumberFieldList.add(textArrayOffsetX); + y = top + 20; + textArrayOffsetX = new GuiNumberField(font, renderables, left + 70, y, 50, 18); + textArrayOffsetX.setNumber(0); + textArrayOffsetX.setTooltip(new TextComponent("How much each copy is shifted.")); + arrayNumberFieldList.add(textArrayOffsetX); - textArrayOffsetY = new GuiNumberField(id++, id++, id++, fontRenderer, buttonList, left + 140, y, 50, 18); - textArrayOffsetY.setNumber(0); - textArrayOffsetY.setTooltip("How much each copy is shifted."); - arrayNumberFieldList.add(textArrayOffsetY); + textArrayOffsetY = new GuiNumberField(font, renderables, left + 140, y, 50, 18); + textArrayOffsetY.setNumber(0); + textArrayOffsetY.setTooltip(new TextComponent("How much each copy is shifted.")); + arrayNumberFieldList.add(textArrayOffsetY); - textArrayOffsetZ = new GuiNumberField(id++, id++, id++, fontRenderer, buttonList, left + 210, y, 50, 18); - textArrayOffsetZ.setNumber(0); - textArrayOffsetZ.setTooltip("How much each copy is shifted."); - arrayNumberFieldList.add(textArrayOffsetZ); + textArrayOffsetZ = new GuiNumberField(font, renderables, left + 210, y, 50, 18); + textArrayOffsetZ.setNumber(0); + textArrayOffsetZ.setTooltip(new TextComponent("How much each copy is shifted.")); + arrayNumberFieldList.add(textArrayOffsetZ); - y = top + 50; - textArrayCount = new GuiNumberField(id++, id++, id++, fontRenderer, buttonList, left + 55, y, 50, 18); - textArrayCount.setNumber(5); - textArrayCount.setTooltip("How many copies should be made."); - arrayNumberFieldList.add(textArrayCount); + y = top + 50; + textArrayCount = new GuiNumberField(font, renderables, left + 55, y, 50, 18); + textArrayCount.setNumber(5); + textArrayCount.setTooltip(new TextComponent("How many copies should be made.")); + arrayNumberFieldList.add(textArrayCount); - ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(mc.player); - if (modifierSettings != null) { - Array.ArraySettings a = modifierSettings.getArraySettings(); - buttonArrayEnabled.setIsChecked(a.enabled); - textArrayOffsetX.setNumber(a.offset.getX()); - textArrayOffsetY.setNumber(a.offset.getY()); - textArrayOffsetZ.setNumber(a.offset.getZ()); - textArrayCount.setNumber(a.count); - } + ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(mc.player); + if (modifierSettings != null) { + Array.ArraySettings a = modifierSettings.getArraySettings(); + buttonArrayEnabled.setIsChecked(a.enabled); + textArrayOffsetX.setNumber(a.offset.getX()); + textArrayOffsetY.setNumber(a.offset.getY()); + textArrayOffsetZ.setNumber(a.offset.getZ()); + textArrayCount.setNumber(a.count); + } - setCollapsed(!buttonArrayEnabled.isChecked()); + setCollapsed(!buttonArrayEnabled.isChecked()); + } - return id; - } + public void updateScreen() { + arrayNumberFieldList.forEach(GuiNumberField::update); + } - @Override - public void updateScreen() { - super.updateScreen(); - arrayNumberFieldList.forEach(GuiNumberField::update); - } + @Override + public void drawEntry(PoseStack ms, int slotIndex, int x, int y, int listWidth, int slotHeight, int mouseX, int mouseY, + boolean isSelected, float partialTicks) { + int yy = y; + int offset = 8; - @Override - public void drawEntry(int slotIndex, int x, int y, int listWidth, int slotHeight, int mouseX, int mouseY, - boolean isSelected, float partialTicks) { - super.drawEntry(slotIndex, x, y, listWidth, slotHeight, mouseX, mouseY, isSelected, partialTicks); + buttonArrayEnabled.render(ms, mouseX, mouseY, partialTicks); + if (buttonArrayEnabled.isChecked()) { + buttonArrayEnabled.y = yy; + font.draw(ms, "Array enabled", left + offset, yy + 2, 0xFFFFFF); - int yy = y; - int offset = 8; + yy = y + 20; + font.draw(ms, "Offset", left + offset, yy + 5, 0xFFFFFF); + font.draw(ms, "X", left + 50 + offset, yy + 5, 0xFFFFFF); + textArrayOffsetX.y = yy; + font.draw(ms, "Y", left + 120 + offset, yy + 5, 0xFFFFFF); + textArrayOffsetY.y = yy; + font.draw(ms, "Z", left + 190 + offset, yy + 5, 0xFFFFFF); + textArrayOffsetZ.y = yy; - buttonArrayEnabled.drawButton(this.mc, mouseX, mouseY, partialTicks); - if (buttonArrayEnabled.isChecked()) { - buttonArrayEnabled.y = yy; - fontRenderer.drawString("Array enabled", left + offset, yy + 2, 0xFFFFFF, true); + yy = y + 50; + font.draw(ms, "Count", left + offset, yy + 5, 0xFFFFFF); + textArrayCount.y = yy; - yy = y + 20; - fontRenderer.drawString("Offset", left + offset, yy + 5, 0xFFFFFF, true); - fontRenderer.drawString("X", left + 50 + offset, yy + 5, 0xFFFFFF, true); - textArrayOffsetX.y = yy; - fontRenderer.drawString("Y", left + 120 + offset, yy + 5, 0xFFFFFF, true); - textArrayOffsetY.y = yy; - fontRenderer.drawString("Z", left + 190 + offset, yy + 5, 0xFFFFFF, true); - textArrayOffsetZ.y = yy; + int currentReach = Math.max(-1, getArrayReach()); + int maxReach = ReachHelper.getMaxReach(mc.player); + ChatFormatting reachColor = isCurrentReachValid(currentReach, maxReach) ? ChatFormatting.GRAY : ChatFormatting.RED; + String reachText = "Reach: " + reachColor + currentReach + ChatFormatting.GRAY + "/" + ChatFormatting.GRAY + maxReach; + font.draw(ms, reachText, left + 176 + offset, yy + 5, 0xFFFFFF); - yy = y + 50; - fontRenderer.drawString("Count", left + offset, yy + 5, 0xFFFFFF, true); - textArrayCount.y = yy; + arrayNumberFieldList.forEach(numberField -> numberField.drawNumberField(ms, mouseX, mouseY, partialTicks)); + } else { + buttonArrayEnabled.y = yy; + font.draw(ms, "Array disabled", left + offset, yy + 2, 0x999999); + } - int currentReach = Math.max(-1, getArrayReach()); - int maxReach = ReachHelper.getMaxReach(mc.player); - TextFormatting reachColor = isCurrentReachValid(currentReach, maxReach) ? TextFormatting.GRAY : TextFormatting.RED; - String reachText = "Reach: " + reachColor + currentReach + TextFormatting.GRAY + "/" + TextFormatting.GRAY + maxReach; - fontRenderer.drawString(reachText, left + 176 + offset, yy + 5, 0xFFFFFF, true); + } - arrayNumberFieldList.forEach(numberField -> numberField.drawNumberField(this.mc, mouseX, mouseY, partialTicks)); - } else { - buttonArrayEnabled.y = yy; - fontRenderer.drawString("Array disabled", left + offset, yy + 2, 0x999999, true); - } + public void drawTooltip(PoseStack ms, Screen guiScreen, int mouseX, int mouseY) { + //Draw tooltips last + if (buttonArrayEnabled.isChecked()) { + arrayNumberFieldList.forEach(numberField -> numberField.drawTooltip(ms, scrollPane.parent, mouseX, mouseY)); + } + } - } + @Override + public boolean charTyped(char typedChar, int keyCode) { + super.charTyped(typedChar, keyCode); + for (GuiNumberField numberField : arrayNumberFieldList) { + numberField.charTyped(typedChar, keyCode); + } + return true; + } - public void drawTooltip(GuiScreen guiScreen, int mouseX, int mouseY) { - //Draw tooltips last - if (buttonArrayEnabled.isChecked()) - { - arrayNumberFieldList.forEach(numberField -> numberField.drawTooltip(scrollPane.parent, mouseX, mouseY)); - } - } + @Override + public boolean mousePressed(int slotIndex, int mouseX, int mouseY, int mouseEvent, int relativeX, int relativeY) { + arrayNumberFieldList.forEach(numberField -> numberField.mouseClicked(mouseX, mouseY, mouseEvent)); - @Override - public void updatePosition(int slotIndex, int x, int y, float partialTicks) { - super.updatePosition(slotIndex, x, y, partialTicks); - } + boolean insideArrayEnabledLabel = mouseX >= left && mouseX < right && relativeY >= -2 && relativeY < 12; - @Override - public void keyTyped(char typedChar, int keyCode) throws IOException { - super.keyTyped(typedChar, keyCode); - for (GuiNumberField numberField : arrayNumberFieldList) { - numberField.keyTyped(typedChar, keyCode); - } - } + if (insideArrayEnabledLabel) { + buttonArrayEnabled.playDownSound(this.mc.getSoundManager()); + buttonArrayEnabled.onClick(mouseX, mouseY); + } - @Override - public boolean mousePressed(int slotIndex, int mouseX, int mouseY, int mouseEvent, int relativeX, int relativeY) { - super.mousePressed(slotIndex, mouseX, mouseY, mouseEvent, relativeX, relativeY); - arrayNumberFieldList.forEach(numberField -> numberField.mouseClicked(mouseX, mouseY, mouseEvent)); + return true; + } - boolean insideArrayEnabledLabel = mouseX >= left && mouseX < right && relativeY >= -2 && relativeY < 12; + public Array.ArraySettings getArraySettings() { + boolean arrayEnabled = buttonArrayEnabled.isChecked(); + BlockPos arrayOffset = new BlockPos(0, 0, 0); + try { + arrayOffset = new BlockPos(textArrayOffsetX.getNumber(), textArrayOffsetY.getNumber(), textArrayOffsetZ.getNumber()); + } catch (NumberFormatException | NullPointerException ex) { + EffortlessBuilding.log(mc.player, "Array offset not a valid number."); + } - if (insideArrayEnabledLabel) { - buttonArrayEnabled.setIsChecked(!buttonArrayEnabled.isChecked()); - buttonArrayEnabled.playPressSound(this.mc.getSoundHandler()); - actionPerformed(buttonArrayEnabled); - } + int arrayCount = 5; + try { + arrayCount = (int) textArrayCount.getNumber(); + } catch (NumberFormatException | NullPointerException ex) { + EffortlessBuilding.log(mc.player, "Array count not a valid number."); + } - return true; - } + return new Array.ArraySettings(arrayEnabled, arrayOffset, arrayCount); + } - @Override - public void actionPerformed(GuiButton button) { - super.actionPerformed(button); - if (button == buttonArrayEnabled) { - setCollapsed(!buttonArrayEnabled.isChecked()); - } - arrayNumberFieldList.forEach(numberField -> numberField.actionPerformed(button)); - } + @Override + protected String getName() { + return "Array"; + } - public Array.ArraySettings getArraySettings() { - boolean arrayEnabled = buttonArrayEnabled.isChecked(); - BlockPos arrayOffset = new BlockPos(0, 0, 0); - try { - arrayOffset = new BlockPos(textArrayOffsetX.getNumber(), textArrayOffsetY.getNumber(), textArrayOffsetZ.getNumber()); - } catch (NumberFormatException | NullPointerException ex) { - EffortlessBuilding.log(mc.player, "Array offset not a valid number."); - } + @Override + protected int getExpandedHeight() { + return 80; + } - int arrayCount = 5; - try { - arrayCount = (int) textArrayCount.getNumber(); - } catch (NumberFormatException | NullPointerException ex) { - EffortlessBuilding.log(mc.player, "Array count not a valid number."); - } + private int getArrayReach() { + try { + //find largest offset + double x = Math.abs(textArrayOffsetX.getNumber()); + double y = Math.abs(textArrayOffsetY.getNumber()); + double z = Math.abs(textArrayOffsetZ.getNumber()); + double largestOffset = Math.max(Math.max(x, y), z); + return (int) (largestOffset * textArrayCount.getNumber()); + } catch (NumberFormatException | NullPointerException ex) { + return -1; + } + } - return new Array.ArraySettings(arrayEnabled, arrayOffset, arrayCount); - } - - @Override - protected String getName() { - return "Array"; - } - - @Override - protected int getExpandedHeight() { - return 80; - } - - private int getArrayReach() { - try - { - //find largest offset - double x = Math.abs(textArrayOffsetX.getNumber()); - double y = Math.abs(textArrayOffsetY.getNumber()); - double z = Math.abs(textArrayOffsetZ.getNumber()); - double largestOffset = Math.max(Math.max(x, y), z); - return (int) (largestOffset * textArrayCount.getNumber()); - } catch (NumberFormatException | NullPointerException ex) { - return -1; - } - } - - private boolean isCurrentReachValid(int currentReach, int maxReach) { - return currentReach <= maxReach && currentReach > -1; - } + private boolean isCurrentReachValid(int currentReach, int maxReach) { + return currentReach <= maxReach && currentReach > -1; + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/buildmodifier/MirrorSettingsGui.java b/src/main/java/nl/requios/effortlessbuilding/gui/buildmodifier/MirrorSettingsGui.java index fb76219..8d942b5 100644 --- a/src/main/java/nl/requios/effortlessbuilding/gui/buildmodifier/MirrorSettingsGui.java +++ b/src/main/java/nl/requios/effortlessbuilding/gui/buildmodifier/MirrorSettingsGui.java @@ -1,299 +1,285 @@ package nl.requios.effortlessbuilding.gui.buildmodifier; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.text.TextFormatting; -import net.minecraftforge.fml.client.config.GuiCheckBox; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.components.Widget; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Button; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.phys.Vec3; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.ChatFormatting; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmodifier.Mirror; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; -import nl.requios.effortlessbuilding.gui.elements.GuiCollapsibleScrollEntry; -import nl.requios.effortlessbuilding.gui.elements.GuiIconButton; -import nl.requios.effortlessbuilding.gui.elements.GuiNumberField; -import nl.requios.effortlessbuilding.gui.elements.GuiScrollPane; +import nl.requios.effortlessbuilding.gui.elements.*; import nl.requios.effortlessbuilding.helper.ReachHelper; -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +@SuppressWarnings("Duplicates") +@OnlyIn(Dist.CLIENT) public class MirrorSettingsGui extends GuiCollapsibleScrollEntry { - protected static final ResourceLocation BUILDING_ICONS = new ResourceLocation(EffortlessBuilding.MODID, "textures/gui/building_icons.png"); + protected static final ResourceLocation BUILDING_ICONS = new ResourceLocation(EffortlessBuilding.MODID, "textures/gui/building_icons.png"); - protected List mirrorButtonList = new ArrayList<>(); - protected List mirrorIconButtonList = new ArrayList<>(); - protected List mirrorNumberFieldList = new ArrayList<>(); + protected List