diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f811f6a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Disable autocrlf on generated files, they always generate with LF +# Add any extra files or paths here to make git stop saying they +# are changed when only line endings change. +src/generated/**/.cache/cache text eol=lf +src/generated/**/*.json text eol=lf diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000..eefca22 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,36 @@ +name: Java CI with Gradle + +on: [push] +jobs: + + jdk17: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Setup JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Build with Gradle + uses: gradle/gradle-build-action@v2 + with: + arguments: build + + - name: Locate built JARfile + id: jar + run: echo "jarfile=$(find build/libs/ -name "*.jar" -not -name "*slim*" -not -name "*source*")" >> $GITHUB_OUTPUT + + - name: Set Artifact name + id: jarname + run: echo "jarname=$(find build/libs/ -name "*.jar" -not -name "*slim*" -not -name "*source*" | sed 's:.*/::')" >> $GITHUB_OUTPUT + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: ${{ steps.jarname.outputs.jarname }} + path: ${{ steps.jar.outputs.jarfile }} diff --git a/.gitignore b/.gitignore index 2c770e0..12f8644 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ build # other eclipse run + +# Files from Forge MDK +forge*changelog.txt diff --git a/CREDITS.txt b/CREDITS.txt new file mode 100644 index 0000000..a70c53d --- /dev/null +++ b/CREDITS.txt @@ -0,0 +1,65 @@ +Minecraft Forge: Credits/Thank You + +Forge is a set of tools and modifications to the Minecraft base game code to assist +mod developers in creating new and exciting content. It has been in development for +several years now, but I would like to take this time thank a few people who have +helped it along it's way. + +First, the people who originally created the Forge projects way back in Minecraft +alpha. Eloraam of RedPower, and SpaceToad of Buildcraft, without their acceptiance +of me taking over the project, who knows what Minecraft modding would be today. + +Secondly, someone who has worked with me, and developed some of the core features +that allow modding to be as functional, and as simple as it is, cpw. For developing +FML, which stabelized the client and server modding ecosystem. As well as the base +loading system that allows us to modify Minecraft's code as elegently as possible. + +Mezz, who has stepped up as the issue and pull request manager. Helping to keep me +sane as well as guiding the community into creating better additions to Forge. + +Searge, Bspks, Fesh0r, ProfMobious, and all the rest over on the MCP team {of which +I am a part}. For creating some of the core tools needed to make Minecraft modding +both possible, and as stable as can be. + On that note, here is some specific information of the MCP data we use: + * Minecraft Coder Pack (MCP) * + Forge Mod Loader and Minecraft Forge have permission to distribute and automatically + download components of MCP and distribute MCP data files. This permission is not + transitive and others wishing to redistribute the Minecraft Forge source independently + should seek permission of MCP or remove the MCP data files and request their users + to download MCP separately. + +And lastly, the countless community members who have spent time submitting bug reports, +pull requests, and just helping out the community in general. Thank you. + +--LexManos + +========================================================================= + +This is Forge Mod Loader. + +You can find the source code at all times at https://github.com/MinecraftForge/MinecraftForge/tree/1.12.x/src/main/java/net/minecraftforge/fml + +This minecraft mod is a clean open source implementation of a mod loader for minecraft servers +and minecraft clients. + +The code is authored by cpw. + +It began by partially implementing an API defined by the client side ModLoader, authored by Risugami. +http://www.minecraftforum.net/topic/75440- +This support has been dropped as of Minecraft release 1.7, as Risugami no longer maintains ModLoader. + +It also contains suggestions and hints and generous helpings of code from LexManos, author of MinecraftForge. +http://www.minecraftforge.net/ + +Additionally, it contains an implementation of topological sort based on that +published at http://keithschwarz.com/interesting/code/?dir=topological-sort + +It also contains code from the Maven project for performing versioned dependency +resolution. http://maven.apache.org/ + +It also contains a partial repackaging of the javaxdelta library from http://sourceforge.net/projects/javaxdelta/ +with credit to it's authors. + +Forge Mod Loader downloads components from the Minecraft Coder Pack +(http://mcp.ocean-labs.de/index.php/Main_Page) with kind permission from the MCP team. + diff --git a/LICENSE INFO.txt b/LICENSE INFO.txt deleted file mode 100644 index adcbe25..0000000 --- a/LICENSE INFO.txt +++ /dev/null @@ -1,13 +0,0 @@ -This mod contains sounds under the Attribution 3.0 license, sounds are from: - -http://soundbible.com/1040-Zombie-Gets-Attacked.html -http://soundbible.com/1043-Zombie-On-The-Loose.html -http://soundbible.com/1034-Zombie-Demon-Spawn.html -http://soundbible.com/1059-Mummy-Zombie.html -http://soundbible.com/1351-Horror-Movie-Ambiance.html -http://soundbible.com/1024-Zombie-Back-From-Dead.html -http://soundbible.com/1030-Zombie-Attack-Walk.html - -Also contains public domain license sounds from: - -https://www.freesound.org/people/PaulMorek/packs/12519/ \ No newline at end of file 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 975fcce..0000000 --- a/README.md +++ /dev/null @@ -1,11 +0,0 @@ -## Description - -A mod that adds various new types of awareness to zombies and other mobs, also adds extra spawning if enable, all highly configurable - -Requires https://github.com/Corosauce/CoroUtil and the forge and mcp snapshot specified in build.gradle to build - -## Pull requests - -- Target the default branch only, that is the current actively maintained branch. -- Focus on 1 feature per branch -- Keep it clean, no unnecessary changes \ No newline at end of file diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..344bca7 --- /dev/null +++ b/README.txt @@ -0,0 +1,46 @@ + +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 "un-renamed" MCP source code (aka +SRG Names) - this means that you will not be able to read them directly against +normal code. + +Setup Process: +============================== + +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. + +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 process again. + +Mapping Names: +============================= +By default, the MDK is configured to use the official mapping names from Mojang for methods and fields +in the Minecraft codebase. These names are covered by a specific license. All modders should be aware of this +license, if you do not agree with it you can change your mapping names to other crowdsourced names in your +build.gradle. For the latest license text, refer to the mapping file itself, or the reference copy here: +https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md + +Additional Resources: +========================= +Community Documentation: http://mcforge.readthedocs.io/en/latest/gettingstarted/ +LexManos' Install Video: https://www.youtube.com/watch?v=8VEdtQLuLO0 +Forge Forum: https://forums.minecraftforge.net/ +Forge Discord: https://discord.gg/UvedJ9m \ No newline at end of file diff --git a/build.gradle b/build.gradle index 20dace5..03504ae 100644 --- a/build.gradle +++ b/build.gradle @@ -1,109 +1,251 @@ buildscript { repositories { - jcenter() - maven { url = "http://files.minecraftforge.net/maven" } + maven { + url "https://cursemaven.com" + content { + includeGroup "curse.maven" + } + } + mavenCentral() + maven { + name = 'sponge' + url = 'https://repo.spongepowered.org/repository/maven-public/' + } + maven { + name = "sonatype" + url = "https://oss.sonatype.org/content/repositories/snapshots/" + } + maven { + name = 'parchment' + url = 'https://maven.parchmentmc.org' + } + maven { + name = 'forge' + url = 'https://files.minecraftforge.net/maven' + } } dependencies { - classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' + classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true + classpath "org.spongepowered:mixingradle:0.7-SNAPSHOT" + } +} + +repositories { + //JEI + maven { url 'https://dvs1.progwml6.com/files/maven' } + /*maven { + url = "https://maven.speiger.com/repository/main" + }*/ + //GT. + maven { url 'https://jitpack.io' } + + //Needed for intellij + maven { + name = 'sponge' + url = 'https://repo.spongepowered.org/repository/maven-public/' + } + maven { + url "https://www.cursemaven.com" + content { + includeGroup "curse.maven" + } } } -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' +apply plugin: 'org.spongepowered.mixin' -version = "1.0" -group= "zombieawareness" // http://maven.apache.org/guides/mini/guide-naming-conventions.html -archivesBaseName = "zombieawareness" +ext.buildnumber = 0 +ext.projversion = 0 -sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. -compileJava { - sourceCompatibility = targetCompatibility = '1.8' +project.projversion = '1.18.1-1.12.3' +group = 'com.corosus.zombieawareness' // http://maven.apache.org/guides/mini/guide-naming-conventions.html +archivesBaseName = 'zombieawareness' + +if (System.getenv('GITHUB_RUN_NUMBER')) { + project.buildnumber = System.getenv('GITHUB_RUN_NUMBER') + version = "${projversion}+${buildnumber}-gha" +} else { + version = "${projversion}" } -ext.configFile = file "build.properties" -configFile.withReader { - // Load config. It shall from now be referenced as simply config or project.config - def prop = new Properties() - prop.load(it) - project.ext.config = new ConfigSlurper().parse prop +// Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17. +java.toolchain.languageVersion = JavaLanguageVersion.of(17) + +println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch')) +minecraft { + // The mappings can be changed at any time and must be in the following format. + // Channel: Version: + // snapshot YYYYMMDD Snapshot are built nightly. + // stable # Stables are built at the discretion of the MCP team. + // official MCVersion Official field/method names from Mojang mapping files + // + // You must be aware of the Mojang license when using the 'official' mappings. + // See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md + // + // Use non-default mappings at your own risk. They may not always work. + // Simply re-run your setup task after changing the mappings to update your workspace. + mappings channel: 'official', version: '1.18.1' + + // accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') // Currently, this location cannot be changed from the default. + accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') + + // Default run configurations. + // These can be tweaked, removed, or duplicated as needed. + runs { + client { + workingDirectory project.file('run') + arg "-mixin.config=zombieawareness.mixins.json" + + // Recommended logging data for a userdev environment + // The markers can be added/remove as needed separated by commas. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + property 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + property 'forge.logging.console.level', 'debug' + property 'mixin.env.remapRefMap', 'true' + property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" + + mods { + examplemod { + source sourceSets.main + } + } + } + + server { + workingDirectory project.file('run') + arg "-mixin.config=zombieawareness.mixins.json" + + // Recommended logging data for a userdev environment + // The markers can be added/remove as needed separated by commas. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + property 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + property 'forge.logging.console.level', 'debug' + property 'mixin.env.remapRefMap', 'true' + + mods { + examplemod { + source sourceSets.main + } + } + } + + data { + workingDirectory project.file('run') + + // Recommended logging data for a userdev environment + // The markers can be added/remove as needed separated by commas. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + property 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + 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', 'examplemod', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') + + mods { + examplemod { + source sourceSets.main + } + } + } + } } -configurations { - deployerJars + +// Include resources generated by data generators. +sourceSets.main.resources { srcDir 'src/generated/resources' } + +apply plugin: 'org.spongepowered.mixin' + +mixin { + add sourceSets.main, 'zombieawareness.refmap.json' + config 'zombieawareness.mixins.json' } -version = "${config.minecraft_version}-${config.mod_version}" +repositories { + // Put repositories for dependencies here + // ForgeGradle automatically adds the Forge maven and Maven Central for you -minecraft { - version = "1.12.1-14.22.0.2475" - 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. - // 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 = "snapshot_20170624" - // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. - - replace '${version}', project.version + // If you have mod jar dependencies in ./libs, you can declare them as a repository like so: + flatDir { + dir 'libs' + } } 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 - - // 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' - - // 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' - - // for more info... + // Specify the version of Minecraft to use. If this is any group other than 'net.minecraft', it is assumed + // that the dep is a ForgeGradle 'patcher' dependency, and its patches will be applied. + // The userdev artifact is a special name and will get all sorts of transformations applied to it. + implementation fg.deobf('com.corosus.coroutil:coroutil:1.18.1-1.2.37') + //lazydfu-1.0-1.18+ + implementation fg.deobf("curse.maven:lazydfu-460819:3544496") + minecraft 'net.minecraftforge:forge:1.18.1-39.0.5' + annotationProcessor 'org.spongepowered:mixin:0.8.5-SNAPSHOT:processor' + + // 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 + + // Examples using mod jars from ./libs + // implementation fg.deobf("blank:coolmod-${mc_version}:${coolmod_version}") + + // For more info... // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html // http://www.gradle.org/docs/current/userguide/dependency_management.html - compile files("../forge-1.12.1-CoroUtil/build/classes/main/") - } -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 at runtime. +jar { + manifest { + attributes([ + "Specification-Title" : "zombieawareness", + "Specification-Vendor" : "corosus", + "Specification-Version" : "1", // We are version 1 of ourselves + "Implementation-Title" : project.name, + "Implementation-Version" : project.jar.archiveVersion, + "Implementation-Vendor" : "examplemodsareus", + "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + 'MixinConfigs': 'zombieawareness.mixins.json' + ]) } } +// Example configuration to allow publishing using the maven-publish plugin +// 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 { - tasks.publish.dependsOn 'build' publications { mavenJava(MavenPublication) { - groupId 'com.corosus' - artifactId 'zombieawareness' artifact jar - artifact sourceJar } } - repositories { - maven { url "$buildDir/../../maven" } + maven { + url "file://${project.projectDir}/mcmodsrepo" + } } } diff --git a/build.properties b/build.properties deleted file mode 100644 index 4150a39..0000000 --- a/build.properties +++ /dev/null @@ -1,2 +0,0 @@ -mod_version=1.11.16 -minecraft_version=1.12.1 \ No newline at end of file diff --git a/changelog.txt b/changelog.txt new file mode 100644 index 0000000..0705363 --- /dev/null +++ b/changelog.txt @@ -0,0 +1,121 @@ +Build: 1.18.1-39.0.5 - Mon Dec 13 21:58:30 GMT 2021 + pupnewfster: + Add RenderArmEvent to make overriding just the arm rendering not require copying nearly as much vanilla code (#8254) + +========= +Build: 1.18.1-39.0.4 - Mon Dec 13 21:32:20 GMT 2021 + bageldotjpg: + Add MobEffect tags (#8231) + +========= +Build: 1.18.1-39.0.3 - Mon Dec 13 19:49:00 GMT 2021 + xfacthd: + Log missing or unsupported dependencies (#8218) + +========= +Build: 1.18.1-39.0.2 - Mon Dec 13 19:33:05 GMT 2021 + sciwhiz12: + Fix datagen test for sounds definitions provider (#8249) + +========= +Build: 1.18.1-39.0.1 - Mon Dec 13 19:14:15 GMT 2021 + williewillus: + Fix wrong stage being declared in transition to common (#8267) + +========= +Build: 1.18.1-39.0.0 - Fri Dec 10 19:32:24 GMT 2021 + curle: + Update to 1.18.1 + + Co-Authored by: + - Curle + _ Orion + +========= +Build: 1.18-38.0.17 - Fri Dec 10 09:23:45 GMT 2021 + oriondevelopment: + [CVE-2021-44228]: Update Log4J to fix the security issue inside it. (#8268) + +========= +Build: 1.18-38.0.16 - Wed Dec 08 00:09:40 GMT 2021 + jaredlll08: + Fix KeyMappings only checking if they conflict with themselves. (#8256) + +========= +Build: 1.18-38.0.15 - Sun Dec 05 19:40:15 GMT 2021 + xfacthd: + Fix ChunkWatchEvent not being fired (#8253) + +========= +Build: 1.18-38.0.14 - Sat Dec 04 01:30:30 GMT 2021 + git: + Call handleUpdateTag for BlockEntities again (#8237) + +========= +Build: 1.18-38.0.13 - Fri Dec 03 22:10:25 GMT 2021 + commoble: + Fix test worldgen data (#8248) + +========= +Build: 1.18-38.0.12 - Thu Dec 02 20:16:47 GMT 2021 + lexmanos: + Allow Forge Registries to return key information for overridden objects. Fixes #8230 + +========= +Build: 1.18-38.0.11 - Thu Dec 02 19:17:12 GMT 2021 + curle: + Save Chunk capabilities to the chunk, rather than recursively to the capabilities. + +========= +Build: 1.18-38.0.10 - Thu Dec 02 15:24:47 GMT 2021 + gigaherz: + Make HandshakeConsumer public again. + Fixes #8241 + + gigaherz: + Fix LevelChunk capability attach crash. + Fix client chunks not having capability providers attached. + Add capability attach tests. + +========= +Build: 1.18-38.0.8 - Thu Dec 02 00:44:15 GMT 2021 + curle: + Add missing biomes back to the BiomeDictionary + + curle: + Complete TODO in ShapedRecipe patch causing logspam related to minecraft:air + +========= +Build: 1.18-38.0.6 - Wed Dec 01 22:12:05 GMT 2021 + curle: + Readd Mixin 0.8.5 to fix modules issues. + +========= +Build: 1.18-38.0.5 - Wed Dec 01 16:56:24 GMT 2021 + curle: + Readd PoseStack field to RenderTooltipEvent. + +========= +Build: 1.18-38.0.4 - Wed Dec 01 01:29:57 GMT 2021 + curle: + Fix custom loot serializers using wrong registry names + +========= +Build: 1.18-38.0.3 - Wed Dec 01 01:15:13 GMT 2021 + lexmanos: + Fix DungeonHooks not returning correct values. Fixes dungeons in world spawning pigs. + +========= +Build: 1.18-38.0.2 - Wed Dec 01 00:23:23 GMT 2021 + lexmanos: + Fix dedicated server install. Closes #8226 + Fix example mod + Fix obf issue with records. Closes #8228 + Fix dependencies beingg out of sync from vanilla. Closes #8227 + Disable mixin due to module incompatibility. + +========= +Build: 1.18-38.0.1 - Tue Nov 30 20:56:52 GMT 2021 + gigaherz: + Fix mod resources not loading. + Add BreakingItemParticle.java.patch which I forgot to commit during the porting. 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 30d399d..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 e18cba7..e750102 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon Sep 14 12:28:28 PDT 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14-bin.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..c53aefa --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/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 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 +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 + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$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 + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + 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 new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@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 +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@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 execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/libs/coroutil-1.18.1-1.2.37.jar b/libs/coroutil-1.18.1-1.2.37.jar new file mode 100644 index 0000000..ce7546e Binary files /dev/null and b/libs/coroutil-1.18.1-1.2.37.jar differ diff --git a/src/main/java/ZombieAwareness/ClientProxy.java b/src/main/java/ZombieAwareness/ClientProxy.java deleted file mode 100644 index a4bd8ae..0000000 --- a/src/main/java/ZombieAwareness/ClientProxy.java +++ /dev/null @@ -1,26 +0,0 @@ -package ZombieAwareness; - -import net.minecraft.client.Minecraft; -import net.minecraftforge.fml.client.registry.RenderingRegistry; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -@SideOnly(Side.CLIENT) -public class ClientProxy extends CommonProxy -{ - - public ClientProxy() - { - - } - - @Override - public void init(ZombieAwareness pMod) - { - super.init(pMod); - - - RenderingRegistry.registerEntityRenderingHandler(EntityScent.class, new RenderScent(Minecraft.getMinecraft().getRenderManager())); - - - } -} diff --git a/src/main/java/ZombieAwareness/CommandZA.java b/src/main/java/ZombieAwareness/CommandZA.java deleted file mode 100644 index 7635928..0000000 --- a/src/main/java/ZombieAwareness/CommandZA.java +++ /dev/null @@ -1,111 +0,0 @@ -package ZombieAwareness; - -import net.minecraft.command.CommandBase; -import net.minecraft.command.ICommandSender; -import net.minecraft.command.WrongUsageException; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.world.World; -import CoroUtil.OldUtil; -import CoroUtil.pathfinding.PFQueue; -import CoroUtil.util.CoroUtilMisc; - -public class CommandZA extends CommandBase { - - @Override - public String getName() { - return "za"; - } - - @Override - public String getUsage(ICommandSender icommandsender) { - return "Magic dev method!"; - } - - @Override - public void execute(MinecraftServer server, ICommandSender var1, String[] var2) { - - EntityPlayer player = null; - if (var1 instanceof EntityPlayer) { - player = (EntityPlayer) var1; - } - World world = var1.getEntityWorld(); - int dimension = world.provider.getDimension(); - BlockPos posBlock = var1.getPosition(); - Vec3d posVec = var1.getPositionVector(); - - try { - if (var2.length < 1) - { - //exception throws dont seem to always get sent to player, do it manually - CoroUtilMisc.sendCommandSenderMsg(var1, "Invalid usage, example: '/za set maxZombiesNight 100, /za get count , /za kill ', see ZAMod.cfg for all possible set configurations"); - throw new WrongUsageException("Invalid usage"); - } - else - { - - if (var2[0].equalsIgnoreCase("set")) { - if (var2[1].equalsIgnoreCase("PFQueue")) { - PFQueue.instance = null; - } else { - int intVal = 0; - try { - intVal = Integer.valueOf(var2[2]); - } catch (Exception ex2) { } //silence! - boolean boolVal = Boolean.valueOf(var2[2]); - - if (intVal > 0) { - OldUtil.setPrivateValueBoth(ZombieAwareness.class, ZombieAwareness.instance, var2[1], var2[1], intVal); - } else { - OldUtil.setPrivateValueBoth(ZombieAwareness.class, ZombieAwareness.instance, var2[1], var2[1], boolVal); - } - - CoroUtilMisc.sendCommandSenderMsg(var1, var2[1] + " now set to: " + OldUtil.getPrivateValueBoth(ZombieAwareness.class, ZombieAwareness.instance, var2[1], var2[1])); - } - - - //System.out.println("new setting for max zombies: " + ZombieAwareness.maxZombiesNight); - - //if (var2[0] == "maxZombiesNight") ZombieAwareness.maxZombiesNight = Integer.valueOf(var2[1]); - } else if (var2[0].equalsIgnoreCase("get")) { - if (var2[1].equalsIgnoreCase("time")) { - if (ZAUtil.getWorldData(dimension).lastSpawnSysTime > 0) { - CoroUtilMisc.sendCommandSenderMsg(var1, "last ZA spawn: " + (int)((System.currentTimeMillis() - ZAUtil.getWorldData(dimension).lastSpawnSysTime) / 1000F) + "s"); - } else { - CoroUtilMisc.sendCommandSenderMsg(var1, "none yet"); - } - } else if (var2[1].equalsIgnoreCase("counts")) { - CoroUtilMisc.sendCommandSenderMsg(var1, "surface: " + ZAUtil.getWorldData(dimension).lastMobsCountSurface + ", caves: " + ZAUtil.getWorldData(dimension).lastMobsCountCaves); - } else { - CoroUtilMisc.sendCommandSenderMsg(var1, var2[1] + " set to: " + OldUtil.getPrivateValueBoth(ZombieAwareness.class, ZombieAwareness.instance, var2[1], var2[1])); - } - } else if (var2[0].equalsIgnoreCase("isActive")) { - var1.sendMessage(new TextComponentString((ZAUtil.isZombieAwarenessActive(world) ? "true" : "false"))); - } else if (var2[0].equalsIgnoreCase("profile") && player != null) { - ZAUtil.startProfile(player.getName()); - player.sendMessage(new TextComponentString("ZA Profile started")); - } - - } - } catch (Exception ex) { - System.out.println("Caught ZA command crash!!!"); - ex.printStackTrace(); - } - } - - @Override - public boolean checkPermission(MinecraftServer server, ICommandSender par1ICommandSender) - { - return par1ICommandSender.canUseCommand(this.getRequiredPermissionLevel(), this.getName()); - } - - @Override - public int getRequiredPermissionLevel() { - return 2; - } - -} diff --git a/src/main/java/ZombieAwareness/CommonProxy.java b/src/main/java/ZombieAwareness/CommonProxy.java deleted file mode 100644 index 0b9f9f6..0000000 --- a/src/main/java/ZombieAwareness/CommonProxy.java +++ /dev/null @@ -1,91 +0,0 @@ -package ZombieAwareness; - -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; -import net.minecraftforge.fml.common.FMLCommonHandler; -import net.minecraftforge.fml.common.network.IGuiHandler; -import net.minecraftforge.fml.common.registry.EntityRegistry; - -public class CommonProxy implements IGuiHandler -{ - private int entityId = 0; - - public ZombieAwareness mod; - - public CommonProxy() - { - } - - public void init(ZombieAwareness pMod) - { - mod = pMod; - EntityRegistry.registerModEntity(new ResourceLocation(ZombieAwareness.modID, "EntityScent"), EntityScent.class, "EntityScent", entityId++, pMod, 32, 20, false); - } - - public int getUniqueTextureLoc() - { - return 0; - } - - public int getArmorNumber(String type) - { - return 0; - } - - public int getUniqueTropicraftLiquidID() - { - return 0; - } - - public void loadSounds() - { - } - - public void registerRenderInformation() - { - } - - public void registerTileEntitySpecialRenderer() - { - } - - public void displayRecordGui(String displayText) - { - } - - public World getClientWorld() - { - return null; - } - - @Override - public Object getServerGuiElement(int ID, EntityPlayer player, World world, - int x, int y, int z) - { - return null; - } - - @Override - public Object getClientGuiElement(int ID, EntityPlayer player, World world, - int x, int y, int z) - { - return null; - } - - public void weatherDbg() - { - - } - - public void windDbg() - { - - } - - public Entity getEntByID(int id) { - System.out.println("common getEntByID being used, this is bad"); - return null; - } -} diff --git a/src/main/java/ZombieAwareness/EntityScent.java b/src/main/java/ZombieAwareness/EntityScent.java deleted file mode 100644 index 27332ed..0000000 --- a/src/main/java/ZombieAwareness/EntityScent.java +++ /dev/null @@ -1,165 +0,0 @@ -package ZombieAwareness; - -import io.netty.buffer.ByteBuf; -import net.minecraft.entity.Entity; -import net.minecraft.entity.monster.EntityZombie; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.network.datasync.DataParameter; -import net.minecraft.network.datasync.DataSerializers; -import net.minecraft.network.datasync.EntityDataManager; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.world.World; -import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; -import ZombieAwareness.config.ZAConfig; -import ZombieAwareness.config.ZAConfigClient; - -public class EntityScent extends Entity implements IEntityAdditionalSpawnData { - - /** - * - * age has a static max that counts down - * - * peak strength is for scaling actual current strength based on age scale - * - */ - - //0 == blood node, 1 == sound node, 2 == wander node - public int type = 0; - public boolean isUsed = false; - - private static final DataParameter STRENGTH_PEAK = EntityDataManager.createKey(EntityScent.class, DataSerializers.VARINT); - private static final DataParameter AGE = EntityDataManager.createKey(EntityScent.class, DataSerializers.VARINT); - - public long lastBuffTime = 0; - public float lastMultiply = 1F; - - public static int MAX_AGE = 30*20; - - public EntityScent(World var1) { - super(var1); - this.isImmuneToFire = true; - this.setSize(0.0F, 0.0F); - } - - @Override - protected void entityInit() { - this.getDataManager().register(STRENGTH_PEAK, Integer.valueOf(0)); - this.getDataManager().register(AGE, Integer.valueOf(0)); - } - - @Override - public boolean canBeCollidedWith() { - return false; - } - - @Override - public void setDead() { - super.setDead(); - } - - @Override - protected boolean canTriggerWalking() { - return false; - } - - @Override - public boolean isInRangeToRenderDist(double var1) { - return true; - } - - @Override - public boolean isInRangeToRender3d(double p_145770_1_, double p_145770_3_, - double p_145770_5_) { - return true; - } - - public float getRange() { - float range = (float)getStrengthScaled() / 100.0F * (float)ZAConfig.maxPFRangeSense; - - return range; - } - - public void setStrengthPeak(int strength) { - int strTry = strength; - if (strTry > ZAConfig.senseMaxStrength) { - strTry = ZAConfig.senseMaxStrength; - } - this.dataManager.set(STRENGTH_PEAK, strTry); - this.resetAge(); - } - - public void resetAge() { - this.dataManager.set(AGE, MAX_AGE); - } - - public int getStrengthPeak() { - return this.dataManager.get(STRENGTH_PEAK); - } - - public int getStrengthScaled() { - return (int)((double)this.dataManager.get(this.STRENGTH_PEAK) * getAgeScale()); - } - - public double getAgeScale() { - return (double)this.dataManager.get(this.AGE) / (double)MAX_AGE; - } - - @Override - public void onUpdate() { - - //TODO: if raining, age smell sense much faster - - int age = this.dataManager.get(AGE); - this.dataManager.set(AGE, --age); - - if(!world.isRemote && age <= 0) { - this.setDead(); - } - - boolean scentDebug = ZAConfigClient.client_debugSensesVisual; - if (scentDebug) { - if (world.isRemote) { - if (world.getTotalWorldTime()/*+this.getEntityId()*/ % 5 == 0) { - for (int i = 0; i < getStrengthScaled() / 10; i++) { - double range = 1D; - double x = posX - world.rand.nextDouble() / 2 + world.rand.nextDouble(); - double y = posY - world.rand.nextDouble() / 2 + world.rand.nextDouble(); - double z = posZ - world.rand.nextDouble() / 2 + world.rand.nextDouble(); - if (type == 0) { - world.spawnParticle(EnumParticleTypes.HEART, true, x, y, z, 0, 0, 0); - } else if (type == 1) { - world.spawnParticle(EnumParticleTypes.NOTE, true, x, y, z, 0, 0, 0); - } else if (type == 2) { - world.spawnParticle(EnumParticleTypes.REDSTONE, true, x, y, z, 0, 0, 0); - } - - } - } - } - } - } - - @Override - public void writeEntityToNBT(NBTTagCompound var1) { - var1.setInteger("age", this.dataManager.get(AGE)); - var1.setInteger("strengthpeak", this.dataManager.get(STRENGTH_PEAK)); - var1.setInteger("type", type); - } - - @Override - public void readEntityFromNBT(NBTTagCompound var1) { - this.dataManager.set(AGE, var1.getInteger("age")); - this.dataManager.set(STRENGTH_PEAK, var1.getInteger("strengthpeak")); - type = var1.getInteger("type"); - } - - @Override - public void writeSpawnData(ByteBuf data) { - data.writeInt(this.type); - } - - @Override - public void readSpawnData(ByteBuf data) { - type = data.readInt(); - } -} diff --git a/src/main/java/ZombieAwareness/RenderScent.java b/src/main/java/ZombieAwareness/RenderScent.java deleted file mode 100644 index 1117e3d..0000000 --- a/src/main/java/ZombieAwareness/RenderScent.java +++ /dev/null @@ -1,149 +0,0 @@ -package ZombieAwareness; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.client.renderer.entity.Render; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityLiving; -import net.minecraft.util.EnumBlockRenderType; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.world.World; - -import org.lwjgl.opengl.GL11; - -import ZombieAwareness.config.ZAConfigClient; - -public class RenderScent extends Render { - - public static ResourceLocation TEXTURE64 = new ResourceLocation(ZombieAwareness.modID + ":textures/entities/bloodx64.png"); - - protected RenderScent(RenderManager renderManager) { - super(renderManager); - } - - public void doRenderNode(Entity var1, double var2, double var4, double var6, float var8, float var9) { - - if (((EntityScent)var1).type == 0) { - float str = (float)((EntityScent)var1).getAgeScale(); - renderBlood(var1, var2, var4, var6, str, var9); - } - } - - @Override - - /** - * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. - */ - protected ResourceLocation getEntityTexture(Entity entity) { - return TEXTURE64; - } - - @Override - public void doRender(Entity var1, double var2, double var4, double var6, float var8, float var9) { - bindEntityTexture(var1); - shadowSize = 1.0F; - GL11.glPushMatrix(); - if (ZAConfigClient.client_renderBlood) { - this.doRenderNode(var1, var2, var4, var6, var8, var9); - } - shadowSize = 0.0F; - GL11.glPopMatrix(); - } - - private World getWorldFromRenderManager() { - return this.renderManager.world; - } - - private void renderBlood(Entity entityIn, double x, double y, double z, float shadowAlpha, float partialTicks) - { - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - this.renderManager.renderEngine.bindTexture(TEXTURE64); - World world = this.getWorldFromRenderManager(); - GlStateManager.depthMask(false); - double f = 0.7D + (shadowAlpha * 0.3D); - - if (entityIn instanceof EntityLiving) - { - EntityLiving entityliving = (EntityLiving)entityIn; - f *= entityliving.getRenderSizeModifier(); - - if (entityliving.isChild()) - { - f *= 0.5F; - } - } - - double d5 = entityIn.lastTickPosX + (entityIn.posX - entityIn.lastTickPosX) * (double)partialTicks; - double d0 = entityIn.lastTickPosY + (entityIn.posY - entityIn.lastTickPosY) * (double)partialTicks; - double d1 = entityIn.lastTickPosZ + (entityIn.posZ - entityIn.lastTickPosZ) * (double)partialTicks; - int i = MathHelper.floor(d5 - (double)f); - int j = MathHelper.floor(d5 + (double)f); - int k = MathHelper.floor(d0 - (double)f); - int l = MathHelper.floor(d0); - int i1 = MathHelper.floor(d1 - (double)f); - int j1 = MathHelper.floor(d1 + (double)f); - double d2 = x - d5; - double d3 = y - d0; - double d4 = z - d1; - Tessellator tessellator = Tessellator.getInstance(); - BufferBuilder vertexbuffer = tessellator.getBuffer(); - vertexbuffer.begin(7, DefaultVertexFormats.POSITION_TEX_COLOR); - - for (BlockPos blockpos : BlockPos.getAllInBoxMutable(new BlockPos(i, k, i1), new BlockPos(j, l, j1))) - { - IBlockState iblockstate = world.getBlockState(blockpos.down()); - - if (iblockstate.getRenderType() != EnumBlockRenderType.INVISIBLE && world.getLightFromNeighbors(blockpos) > 3) - { - this.renderBloodSingle(iblockstate, x, y, z, blockpos, shadowAlpha, f, d2, d3, d4); - } - } - - tessellator.draw(); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - GlStateManager.disableBlend(); - GlStateManager.depthMask(true); - } - - private void renderBloodSingle(IBlockState state, double p_188299_2_, double p_188299_4_, double p_188299_6_, BlockPos p_188299_8_, float p_188299_9_, double p_188299_10_, double p_188299_11_, double p_188299_13_, double p_188299_15_) - { - if (state.isFullCube()) - { - Tessellator tessellator = Tessellator.getInstance(); - BufferBuilder vertexbuffer = tessellator.getBuffer(); - double d0 = p_188299_9_; - - if (d0 >= 0.0D) - { - if (d0 > 1.0D) - { - d0 = 1.0D; - } - - AxisAlignedBB axisalignedbb = state.getBoundingBox(this.getWorldFromRenderManager(), p_188299_8_); - double d1 = (double)p_188299_8_.getX() + axisalignedbb.minX + p_188299_11_; - double d2 = (double)p_188299_8_.getX() + axisalignedbb.maxX + p_188299_11_; - double d3 = (double)p_188299_8_.getY() + axisalignedbb.minY + p_188299_13_ + 0.015625D; - double d4 = (double)p_188299_8_.getZ() + axisalignedbb.minZ + p_188299_15_; - double d5 = (double)p_188299_8_.getZ() + axisalignedbb.maxZ + p_188299_15_; - float f = (float)((p_188299_2_ - d1) / 2.0D / (double)p_188299_10_ + 0.5D); - float f1 = (float)((p_188299_2_ - d2) / 2.0D / (double)p_188299_10_ + 0.5D); - float f2 = (float)((p_188299_6_ - d4) / 2.0D / (double)p_188299_10_ + 0.5D); - float f3 = (float)((p_188299_6_ - d5) / 2.0D / (double)p_188299_10_ + 0.5D); - vertexbuffer.pos(d1, d3, d4).tex((double)f, (double)f2).color(1.0F, 1.0F, 1.0F, (float)d0).endVertex(); - vertexbuffer.pos(d1, d3, d5).tex((double)f, (double)f3).color(1.0F, 1.0F, 1.0F, (float)d0).endVertex(); - vertexbuffer.pos(d2, d3, d5).tex((double)f1, (double)f3).color(1.0F, 1.0F, 1.0F, (float)d0).endVertex(); - vertexbuffer.pos(d2, d3, d4).tex((double)f1, (double)f2).color(1.0F, 1.0F, 1.0F, (float)d0).endVertex(); - } - } - } - -} diff --git a/src/main/java/ZombieAwareness/SoundRegistry.java b/src/main/java/ZombieAwareness/SoundRegistry.java deleted file mode 100644 index 352d3b0..0000000 --- a/src/main/java/ZombieAwareness/SoundRegistry.java +++ /dev/null @@ -1,37 +0,0 @@ -package ZombieAwareness; - -import java.util.HashMap; - -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.SoundEvent; -import net.minecraftforge.fml.common.registry.ForgeRegistries; -import net.minecraftforge.fml.common.registry.GameRegistry; - -public class SoundRegistry { - - private static HashMap lookupStringToEvent = new HashMap(); - - public static void init() { - register("alert"); - register("target"); - register("investigate"); - - - - } - - public static void register(String soundPath) { - ResourceLocation resLoc = new ResourceLocation(ZombieAwareness.modID, soundPath); - SoundEvent event = new SoundEvent(resLoc).setRegistryName(resLoc); - ForgeRegistries.SOUND_EVENTS.register(event); - if (lookupStringToEvent.containsKey(soundPath)) { - System.out.println("ZA SOUNDS WARNING: duplicate sound registration for " + soundPath); - } - lookupStringToEvent.put(soundPath, event); - } - - public static SoundEvent get(String soundPath) { - return lookupStringToEvent.get(soundPath); - } - -} diff --git a/src/main/java/ZombieAwareness/WorldData.java b/src/main/java/ZombieAwareness/WorldData.java deleted file mode 100644 index 6926dc7..0000000 --- a/src/main/java/ZombieAwareness/WorldData.java +++ /dev/null @@ -1,11 +0,0 @@ -package ZombieAwareness; - -public class WorldData { - - public int lastZombieCount; - public int lastMobsCountSurface; - public int lastMobsCountCaves; - public long lastSpawnTime; - public long lastSpawnSysTime; - -} diff --git a/src/main/java/ZombieAwareness/WorldEventListener.java b/src/main/java/ZombieAwareness/WorldEventListener.java deleted file mode 100644 index 4d9ab4f..0000000 --- a/src/main/java/ZombieAwareness/WorldEventListener.java +++ /dev/null @@ -1,111 +0,0 @@ -package ZombieAwareness; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.SoundCategory; -import net.minecraft.util.SoundEvent; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.IWorldEventListener; -import net.minecraft.world.World; -import net.minecraftforge.common.DimensionManager; - -import javax.annotation.Nullable; - -/** - * - * Forge sound event doesnt pass along position, so we are using mc events to get that - * - * @author Corosus - * - */ -public class WorldEventListener implements IWorldEventListener { - - public int dimID = -1; - - public WorldEventListener(int dimID) { - this.dimID = dimID; - } - - @Override - public void notifyBlockUpdate(World worldIn, BlockPos pos, - IBlockState oldState, IBlockState newState, int flags) { - - } - - @Override - public void notifyLightSet(BlockPos pos) { - - } - - @Override - public void markBlockRangeForRenderUpdate(int x1, int y1, int z1, int x2, - int y2, int z2) { - - } - - @Override - public void playSoundToAllNearExcept(@Nullable EntityPlayer player, - SoundEvent soundIn, SoundCategory category, double x, double y, - double z, float volume, float pitch) { - - //there is an edge case where world can be null during world load, just cancel the call - World world = DimensionManager.getWorld(dimID); - if (world == null) return; - - ZAUtil.hookSoundEvent(soundIn, world, x, y, z, volume, pitch); - - } - - @Override - public void playRecord(SoundEvent soundIn, BlockPos pos) { - - } - - @Override - public void spawnParticle(int particleID, boolean ignoreRange, - double xCoord, double yCoord, double zCoord, double xSpeed, - double ySpeed, double zSpeed, int... parameters) { - - } - - @Override - public void onEntityAdded(Entity entityIn) { - - } - - @Override - public void onEntityRemoved(Entity entityIn) { - - } - - @Override - public void broadcastSound(int soundID, BlockPos pos, int data) { - - } - - @Override - public void playEvent(EntityPlayer player, int type, BlockPos blockPosIn, - int data) { - - //there is an edge case where world can be null during world load, just cancel the call - World world = DimensionManager.getWorld(dimID); - if (world == null) return; - - ZAUtil.hookPlayEvent(world, type, blockPosIn, data); - - } - - @Override - public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress) { - - } - - @Override - public void spawnParticle(int p_190570_1_, boolean p_190570_2_, boolean p_190570_3_, double p_190570_4_, - double p_190570_6_, double p_190570_8_, double p_190570_10_, double p_190570_12_, double p_190570_14_, - int... p_190570_16_) { - - } - -} diff --git a/src/main/java/ZombieAwareness/ZAEventHandler.java b/src/main/java/ZombieAwareness/ZAEventHandler.java deleted file mode 100644 index 00211e7..0000000 --- a/src/main/java/ZombieAwareness/ZAEventHandler.java +++ /dev/null @@ -1,137 +0,0 @@ -package ZombieAwareness; - -import CoroUtil.forge.CULog; -import ZombieAwareness.config.ZAConfig; -import net.minecraft.util.EnumHand; -import net.minecraft.world.World; -import net.minecraftforge.common.DimensionManager; -import net.minecraftforge.event.entity.PlaySoundAtEntityEvent; -import net.minecraftforge.event.entity.living.LivingEvent; -import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent; -import net.minecraftforge.event.entity.player.PlayerEvent.BreakSpeed; -import net.minecraftforge.event.entity.player.PlayerEvent.HarvestCheck; -import net.minecraftforge.event.entity.player.PlayerInteractEvent; -import net.minecraftforge.event.world.BlockEvent; -import net.minecraftforge.event.world.WorldEvent; -import net.minecraftforge.fml.common.FMLCommonHandler; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.gameevent.TickEvent; -import net.minecraftforge.fml.common.gameevent.TickEvent.Phase; -import net.minecraftforge.fml.common.gameevent.TickEvent.ServerTickEvent; - -public class ZAEventHandler { - - @SubscribeEvent - public void soundEvent(PlaySoundAtEntityEvent event) { - - try { - /*if (event.getSound().getSoundName().toString().contains("piston")) { - System.out.println(event.getSound().getSoundName().toString()); - }*/ - - if (event.getEntity() != null) { - //moved to world event listener for getting coords - //ZAUtil.soundHook(event.getSound().getSoundName().toString(), event.getEntity().world, (float)event.getEntity().posX, (float)event.getEntity().posY, (float)event.getEntity().posZ, event.getVolume(), event.getPitch()); - } - } catch (Exception ex) { - ex.printStackTrace(); - } - } - - @SubscribeEvent - public void setAttackTarget(LivingSetAttackTargetEvent event) { - if (!event.getEntityLiving().world.isRemote) { - if (!ZAUtil.isZombieAwarenessActive(event.getEntityLiving().world)) return; - ZAUtil.hookSetAttackTarget(event); - } - } - - @SubscribeEvent - public void breakSpeed(BreakSpeed event) { - if (!event.getEntityLiving().world.isRemote) { - if (!ZAUtil.isZombieAwarenessActive(event.getEntityLiving().world)) return; - if (!ZAConfig.blockHittingEvent_Active) return; - //ZombieAwareness.dbg("BreakSpeed event"); - ZAUtil.hookBlockEvent(event, ZAConfig.blockHittingEvent_OddsTo1); - } - } - - @SubscribeEvent - public void harvest(HarvestCheck event) { - if (!event.getEntityLiving().world.isRemote) { - /*if (!ZAUtil.isZombieAwarenessActive(event.getEntityLiving().world)) return; - ZombieAwareness.dbg("HarvestCheck event"); - ZAUtil.hookBlockEvent(event, 3);*/ - } - } - - @SubscribeEvent - public void breakBlock(BlockEvent.HarvestDropsEvent event) { - if (!event.getWorld().isRemote) { - if (!ZAUtil.isZombieAwarenessActive(event.getWorld())) return; - if (!ZAConfig.blockBreakEvent_Active) return; - ZombieAwareness.dbg("HarvestDrops event"); - ZAUtil.handleBlockBasedEvent(event.getHarvester(), event.getWorld(), event.getPos(), 3); - } - } - - @SubscribeEvent - public void interact(PlayerInteractEvent event) { - if (!event.getEntityLiving().world.isRemote) { - if (event.getHand() == EnumHand.MAIN_HAND) { - /** - * event is way too spammy, since i have much greater sound play access now I am going to try and avoid using this event entirely - */ - //ZombieAwareness.dbg("interact event"); - //ZAUtil.hookBlockEvent(event, 3); - } - } - } - - @SubscribeEvent - public void tickServer(ServerTickEvent event) { - - if (event.phase == Phase.START) { - - if (!ZAUtil.isZombieAwarenessActive(DimensionManager.getWorld(0))) return; - - ZombieAwareness.instance.onTick(); - } - } - - @SubscribeEvent - public void worldLoad(WorldEvent.Load event) { - int dimID = event.getWorld().provider.getDimension(); - CULog.dbg("adding ZA world listener for dimID: " + dimID + ", remote?: " + event.getWorld().isRemote); - event.getWorld().addEventListener(new WorldEventListener(dimID)); - } - - @SubscribeEvent - public void tickEntity(LivingEvent.LivingUpdateEvent event) { - if (event.getEntityLiving().world.isRemote) return; - - ZombieAwareness.tickEntity(event.getEntityLiving()); - } - - @SubscribeEvent - public void tickPlayer(TickEvent.PlayerTickEvent event) { - if (event.player.world.isRemote || event.phase == Phase.END) return; - - if (event.player.world.getTotalWorldTime() % ZAConfig.tickRatePlayerLoop == 0) { - - if (event.player.world.provider.getDimension() == 0) { - //ZAUtil.tickPlayerOverworldOnly(event.player); - } - - ZAUtil.tickPlayer(event.player); - } - - } - - @SubscribeEvent - public void tickWorld(TickEvent.WorldTickEvent event) { - if (event.world.isRemote || event.phase == Phase.END) return; - - ZombieAwareness.tickWorld(event.world); - } -} diff --git a/src/main/java/ZombieAwareness/ZAUtil.java b/src/main/java/ZombieAwareness/ZAUtil.java deleted file mode 100644 index f4c2e70..0000000 --- a/src/main/java/ZombieAwareness/ZAUtil.java +++ /dev/null @@ -1,1003 +0,0 @@ -package ZombieAwareness; - -import java.util.*; - -import CoroUtil.difficulty.UtilEntityBuffs; -import CoroUtil.forge.CULog; -import CoroUtil.util.*; -import net.minecraft.block.Block; -import net.minecraft.block.BlockButton; -import net.minecraft.block.BlockLever; -import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; -import net.minecraft.entity.*; -import net.minecraft.entity.ai.attributes.AttributeModifier; -import net.minecraft.entity.monster.EntitySkeleton; -import net.minecraft.entity.monster.EntitySpider; -import net.minecraft.entity.monster.EntityZombie; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; -import net.minecraft.init.SoundEvents; -import net.minecraft.item.Item; -import net.minecraft.item.ItemRecord; -import net.minecraft.pathfinding.PathPoint; -import net.minecraft.util.SoundCategory; -import net.minecraft.util.SoundEvent; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.world.World; -import net.minecraft.world.WorldServer; -import net.minecraft.world.biome.Biome.SpawnListEntry; -import net.minecraftforge.common.DimensionManager; -import net.minecraftforge.event.ForgeEventFactory; -import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent; -import net.minecraftforge.event.entity.player.PlayerEvent; -import net.minecraftforge.fml.common.eventhandler.Event.Result; -import CoroUtil.pathfinding.PFQueue; -import ZombieAwareness.config.ZAConfig; -import ZombieAwareness.config.ZAConfigFeatures; -import ZombieAwareness.config.ZAConfigPlayerLists; -import ZombieAwareness.config.ZAConfigSpawning; - -public class ZAUtil { - - /** - * - * Default settings: - * - * - sound trigger: start at 8 range sense, increase to 32 as more are done - * - scent trigger: start at 24, fade, reset fade if new blood - * - * - sense strength effects: - * - range it can be sensed - * - chance of them sensing it? - * - * - strength * buff - * - if new, use above - * - if exists, apply multiplier - * - * - must have sound triggers: - * - block mine - * - block place eg minecraft:block.gravel.place - * - chest use - * - doors - * - buttons - * - levers - * - * - large sound triggers: - * -- explosions - * -- falling blocks - * -- falling anvils - * -- - * - * for 1.10.2 release - * x whitelist instead of blacklist i think - * x sound sense profile for long distance ones, for explosion, piston, noisy zombie, has data: - * x- sound event or partial string match - * x- max distance spawnable - * x- multiplier - * x buff jukebox, notebox - * x fix interact spam - * - * - */ - - public static Random rand = new Random(); - - public static HashMap lastHealths = new HashMap(); - public static HashMap lastBleedTimes = new HashMap(); - - public static List listSoundProfiles = new ArrayList(); - - public static WeakHashMap lookupLastAlertTime = new WeakHashMap(); - public static long alertDelay = 60*20; - - public static WeakHashMap lookupLastInvestigateTime = new WeakHashMap(); - public static long investigateDelay = 60*20; - - public static HashMap lookupTickableEntities = new HashMap<>(); - - //quick process testing - public static HashMap lookupProcessedEntityCounts = new HashMap<>(); - public static boolean profileActive = false; - public static long profileStartTime = 0; - public static String profileForPlayer = ""; - - public static HashMap lookupWorldData = new HashMap<>(); - - public static boolean debug = false; - - static { - int noisyInteractRange = 30; - double noisyInteractBuff = 1.3D; - - //short dist ones - listSoundProfiles.add(new SoundProfileEntry(SoundEvents.ENTITY_ARROW_HIT_PLAYER, 1.1D)); - listSoundProfiles.add(new SoundProfileEntry(SoundEvents.ENTITY_ARROW_HIT, 1.1D)); - - listSoundProfiles.add(new SoundProfileEntry(SoundEvents.BLOCK_CHEST_CLOSE, noisyInteractBuff).setDistanceMax(noisyInteractRange)); - listSoundProfiles.add(new SoundProfileEntry(SoundEvents.BLOCK_WOODEN_DOOR_CLOSE, noisyInteractBuff).setDistanceMax(noisyInteractRange)); - listSoundProfiles.add(new SoundProfileEntry(SoundEvents.BLOCK_IRON_DOOR_CLOSE, noisyInteractBuff).setDistanceMax(noisyInteractRange)); - listSoundProfiles.add(new SoundProfileEntry(SoundEvents.BLOCK_WOODEN_TRAPDOOR_CLOSE, noisyInteractBuff).setDistanceMax(noisyInteractRange)); - - listSoundProfiles.add(new SoundProfileEntry(".place", noisyInteractBuff)); - listSoundProfiles.add(new SoundProfileEntry("player.burp", 1.1D)); - - //covers all note block sounds - listSoundProfiles.add(new SoundProfileEntry("block.note", noisyInteractBuff).setDistanceMax(64)); - - listSoundProfiles.add(new SoundProfileEntry("lever.click", noisyInteractBuff).setDistanceMax(noisyInteractRange)); - listSoundProfiles.add(new SoundProfileEntry("pressureplate.click", noisyInteractBuff).setDistanceMax(noisyInteractRange)); - listSoundProfiles.add(new SoundProfileEntry("button.click", noisyInteractBuff).setDistanceMax(noisyInteractRange)); - - //long dist ones - if (ZAConfigFeatures.noisyZombies) listSoundProfiles.add(new SoundProfileEntry(SoundEvents.ENTITY_ZOMBIE_AMBIENT, 0.8D, 8*20).setDistanceMax(48)); - if (ZAConfigFeatures.noisyPistons) listSoundProfiles.add(new SoundProfileEntry(SoundEvents.BLOCK_PISTON_EXTEND, 2D, 20).setDistanceMax(128)); - - listSoundProfiles.add(new SoundProfileEntry(SoundEvents.ENTITY_GENERIC_EXPLODE, 3D).setDistanceMax(128)); - - - } - - public static void startProfile(String playerName) { - profileActive = true; - profileForPlayer = playerName; - profileStartTime = DimensionManager.getWorld(0).getTotalWorldTime(); - } - - public static void trackProfile() { - if (profileActive) { - World world = DimensionManager.getWorld(0); - if (world.getTotalWorldTime() > profileStartTime + 10 * 20) { - EntityPlayer player = world.getPlayerEntityByName(profileForPlayer); - if (player != null) { - for (Map.Entry entry : lookupProcessedEntityCounts.entrySet()) { - player.sendMessage(new TextComponentString(entry.getKey() + " : " + entry.getValue())); - } - - } - resetProfile(); - } - } - } - - public static void resetProfile() { - profileActive = false; - lookupProcessedEntityCounts.clear(); - profileForPlayer = ""; - } - - public static SoundProfileEntry getFirstEntry(String sound) { - for (SoundProfileEntry entry : listSoundProfiles) { - if (entry.getSoundName().equals(sound)) { - return entry; - } else if (entry.isPartialMatchOnly() && sound.contains(entry.getSoundName())) { - return entry; - } - } - return null; - } - - public static void tickPlayer(EntityPlayer player) { - - if (!ZAConfigPlayerLists.whiteListUsedExtraSpawning || ZAConfigPlayerLists.whitelistExtraSpawning.contains(CoroUtilEntity.getName(player))) { - if (ZAConfigFeatures.extraSpawningSurface) { - if (!player.world.isDaytime()) { - if (getWorldData(player.world.provider.getDimension()).lastMobsCountSurface < ZAConfigSpawning.extraSpawningSurfaceMaxCount) { - if (ZAConfigSpawning.extraSpawningSurfaceRandomPool <= 0 || rand.nextInt(ZAConfigSpawning.extraSpawningSurfaceRandomPool) == 0) { - spawnNewMobSurface(player); - } - } - } - } - - if (ZAConfigFeatures.extraSpawningCave) { - if (getWorldData(player.world.provider.getDimension()).lastMobsCountCaves < ZAConfigSpawning.extraSpawningCavesMaxCount) { - if (ZAConfigSpawning.extraSpawningCavesRandomPool <= 0 || rand.nextInt(ZAConfigSpawning.extraSpawningCavesRandomPool) == 0) { - spawnNewMobCave(player); - } - } - } - } - - if (ZAConfigFeatures.wanderingHordes) { - if (rand.nextInt(25) == 0) { - spawnWaypoint(player); - } - } - - if (ZAConfigFeatures.awareness_Scent && !player.isCreative()) { - int lastHealth = lastHealths.containsKey(CoroUtilEntity.getName(player)) ? lastHealths.get(CoroUtilEntity.getName(player)) : 0; - Long lastBleedTime = lastBleedTimes.containsKey(CoroUtilEntity.getName(player)) ? lastBleedTimes.get(CoroUtilEntity.getName(player)) : 0L; - - Vec3d pos = new Vec3d(player.posX, player.posY, player.posZ); - - if((int)player.getHealth() != lastHealth) { - if(player.getHealth() < lastHealth) { - EntityScent scent = spawnOrBuffSenseAtPos(player.world, pos, EnumSenseType.SCENT_BLOOD, ZAConfig.scentStrength); - ZombieAwareness.dbg("spawned or buffed scent sense from damage: " + scent.getStrengthPeak()); - } - - lastHealth = (int) player.getHealth(); - } - - lastHealths.put(CoroUtilEntity.getName(player), lastHealth); - - if(player.getHealth() / player.getMaxHealth() < 0.6F && lastBleedTime < System.currentTimeMillis()) { - lastBleedTime = System.currentTimeMillis() + 30000L; - lastBleedTimes.put(CoroUtilEntity.getName(player), lastBleedTime); - EntityScent scent = spawnOrBuffSenseAtPos(player.world, pos, EnumSenseType.SCENT_BLOOD, ZAConfig.scentStrength); - ZombieAwareness.dbg("spawned or buffed scent sense from bleeding: " + scent.getStrengthPeak()); - } - } - } - - public static void giveRandomSpeedBoost(EntityLiving ent) { - - if (ZAConfig.zombieRandSpeedBoost > 0) { - double randBoost = ent.world.rand.nextDouble() * ZAConfig.zombieRandSpeedBoost; - AttributeModifier speedBoostModifier = new AttributeModifier(CoroUtilAttributes.SPEED_BOOST_UUID, "ZA speed boost", randBoost, EnumAttribModifierType.INCREMENT_MULTIPLY_BASE.ordinal()); - if (!ent.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).hasModifier(speedBoostModifier)) { - ent.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).applyModifier(speedBoostModifier); - } - } - - } - - public static void huntTarget(EntityLiving ent, EntityLivingBase targ, int pri) { - CoroUtilPath.tryMoveToEntityLivingLongDist(ent, targ, 1); - if (ent instanceof EntityLiving) (ent).setAttackTarget(targ); - } - - public static void huntTarget(EntityLiving ent, EntityLivingBase targ) { - huntTarget(ent, targ, 0); - } - - public static boolean isEnemy(Entity ent, Entity targ) { - return isEnemy(ent, targ, false); - } - - public static boolean isEnemy(Entity ent, Entity targ, boolean omniTarget) { - if (targ instanceof EntityLivingBase) { - if (targ instanceof EntityPlayer) { - if (!((EntityPlayer) targ).capabilities.isCreativeMode && ((EntityPlayer) targ).getActivePotionEffect(MobEffects.INVISIBILITY) == null) { - if (!omniTarget) { - return true; - } else if (ZAConfigPlayerLists.whiteListUsedOmniscient) { - if (ZAConfigPlayerLists.whitelistOmniscientTargettedPlayers.contains(CoroUtilEntity.getName(((EntityPlayer) targ)))) { - if (ZAConfig.debugConsoleOmniscient) ZombieAwareness.dbg(CoroUtilEntity.getName((EntityPlayer) targ) + " targetting omnisciently by " + ent); - return true; - } - } else { - return true; - } - } - } - return false; - } - return false; - } - - public static boolean sanityCheck(Entity ent, Entity entity1) { - return true; - } - - public static void tickAI(EntityLiving ent) { - - if (profileActive) { - int val = 0; - if (lookupProcessedEntityCounts.containsKey(ent.getClass())) { - val = lookupProcessedEntityCounts.get(ent.getClass()); - } - lookupProcessedEntityCounts.put(ent.getClass(), val + 1); - } - - if (ZAConfig.debugConsoleSuperDetailed) ZombieAwareness.dbg("ZA DBG: Ticking: " + ent); - - //A more performance friendly omniscient, only runs it when no target, but still allows for smaller ranged retargetting - //adding entity ID onto world time to stagger processing per entity more, should improve TPS - if ((ent.world.getTotalWorldTime() + ent.getEntityId()) % 40 == 0) { - if (ZAConfig.omniscient && ent.getAttackTarget() == null) { - ai_FindTarget(ent, true); - } else { - ai_FindTarget(ent, false); - } - } - - if (PFQueue.instance == null) { - new PFQueue(ent.world); - } - - EntityScent senseTracked = null; - - if (ent.getAttackTarget() == null && (ent.getNavigator().noPath())) { - //Find player made senses - if (!ZAConfig.awareness_Light_OnlyZombies || (ent instanceof EntityZombie)) { - if (!ZAConfigFeatures.awareness_Light || !ai_FindLightSource(ent)) { - if (ent.world.rand.nextInt(3) == 0) { - senseTracked = ai_FindSense(ent, true); - } - - } - } else { - senseTracked = ai_FindSense(ent); - } - } - - if (senseTracked != null && ent.getNavigator().getPath() != null) { - PathPoint pathTo = ent.getNavigator().getPath().getFinalPathPoint(); - if (pathTo != null) { - EntityPlayer player = getClosestPlayer(ent.world, pathTo.x, pathTo.y, pathTo.z, 6D); - if (player != null) { - //tryPlayInvestigateSound(ent, new Vec3d(ent.posX, ent.posY, ent.posZ)); - tryPlayInvestigateSound(ent, new Vec3d(pathTo.x, pathTo.y, pathTo.z)); - } - - } - - } - - tickCustomMob(ent); - } - - public static void tickCustomMob(EntityLiving ent) { - if (ZAConfigFeatures.wanderingHordes) { - if (ent instanceof EntitySpider) { - if (ent.getPassengers().size() > 0 && ent.getPassengers().get(0) instanceof EntitySkeleton) { - if (ent.world.rand.nextInt(100) == 0) { - spawnWaypoint(ent); - } - } - } - } - } - - public static boolean ai_FindLightSource(EntityLiving ent) { - - if (ent.world.isDaytime()) return false; - - if (ent.world.rand.nextInt(3) == 0) { - - int lightValueAtEntity = ent.world.getLightFromNeighbors(ent.getPosition()); - - Random rand = new Random(); - - int size; - - for (int i = 0; i < 4; i++) { - EntityPlayer entP = getClosestPlayerToEntity(ent.world, ent, 999); - if (entP != null) { - - size = 32 * (i+1); - - int rX = MathHelper.floor(entP.posX + (rand.nextInt(size) - (size/2))); - int rY = MathHelper.floor(entP.posY + (rand.nextInt(size/2) - (size/4))); - int rZ = MathHelper.floor(entP.posZ + (rand.nextInt(size) - (size/2))); - - BlockPos pos = new BlockPos(rX, rY, rZ); - - if (!ent.world.isBlockLoaded(pos)) continue; - - int lightValue = entP.world.getLightFromNeighbors(pos); - - //if bright enough and also as bright or brighter than where they are currently - if (lightValue > 4 && lightValue >= lightValueAtEntity) { - if (((ent.world.rand.nextInt(5) == 0 && ent.getDistanceToEntity(entP) > 64) || - ent.world.rayTraceBlocks(new Vec3d(ent.posX, ent.posY + (double)ent.getEyeHeight(), ent.posZ), new Vec3d(rX, rY, rZ)) == null)) { - //CULog.dbg("path to light source"); - if (CoroUtilPath.tryMoveToXYZLongDist(ent, rX, rY, rZ, 1)) { - //ZombieAwareness.dbg("pathing to lightsource at " + rX + ", " + rY + ", " + rZ + " - " + ent); - } - return true; - } - } - } - } - } - - return false; - } - - public static EntityScent ai_FindSense(EntityLiving ent) { - return ai_FindSense(ent, true); - } - - public static EntityScent ai_FindSense(EntityLiving ent, boolean includeWaypoints) { - - EntityScent var3 = getSenseNearEntity(ent); - - if(var3 != null) { - if (includeWaypoints || var3.type != 2) { - if (CoroUtilPath.tryMoveToEntityLivingLongDist(ent, var3, 1)) { - //ZombieAwareness.dbg("ai_FindSense call, type: " + ((EntityScent)var3).type + " - " + ent.getName() + " -> " + var3.getPosition()); - return var3; - } - } - } - - return null; - } - - public static boolean ai_FindTarget(EntityLiving ent, boolean omniscient) { - long huntRange = ZAConfig.sightRange; - - if (omniscient) huntRange = 512; - - if ((ent.getAttackTarget() == null || ent.world.rand.nextInt(100) == 0)) { - boolean found = false; - Entity clEnt = null; - float closest = 9999F; - List list = ent.world.getEntitiesWithinAABBExcludingEntity(ent, ent.getEntityBoundingBox().grow(huntRange, huntRange/2, huntRange)); - for(int j = 0; j < list.size(); j++) - { - Entity entity1 = (Entity)list.get(j); - - //for new calmed zombies, keep them from targetting/pathing towards player - //just blacklisting all of them from this feature, not checking if calm currently - if (ent.getClass().getSimpleName().equals("EntityZombiePlayer")) { - continue; - } - - if(isEnemy(ent, entity1, omniscient)) - { - if (omniscient || (ZAConfig.seeThroughWalls || ((EntityLivingBase) entity1).canEntityBeSeen(ent))) { - if (sanityCheck(ent, entity1)) { - float dist = ent.getDistanceToEntity(entity1); - if (dist < closest) { - closest = dist; - clEnt = entity1; - } - } - } - } - } - if (clEnt != null) { - huntTarget(ent, (EntityLivingBase)clEnt); - return true; - } - } - return false; - } - - /** - * Gets sense within range provided sense is strong enough, has random chance and doesnt always return closest sense - * - * @param entSource - * @return - */ - public static EntityScent getSenseNearEntity(Entity entSource) { - List listEnts = entSource.world.getEntitiesWithinAABBExcludingEntity(entSource, entSource.getEntityBoundingBox().grow((double)ZAConfig.maxPFRangeSense, (double)ZAConfig.maxPFRangeSense, (double)ZAConfig.maxPFRangeSense)); - - EntityScent entBest = null; - - for(int i = 0; i < listEnts.size(); ++i) { - Entity entCheck = listEnts.get(i); - - if (entCheck instanceof EntityScent) { - - double dist = entSource.getDistanceToEntity(entCheck); - - if (dist < ((EntityScent)entCheck).getRange() && dist > 5.0F && entSource.world.rand.nextInt(20) == 0) { - entBest = (EntityScent) entCheck; - return entBest; - } - } - } - - return entBest; - } - - public static void hookSoundEvent(SoundEvent sound, World world, double x, double y, double z, float volume, float pitch) { - - if (!ZAConfigFeatures.awareness_Sound) { - return; - } - - if (world.isRemote || sound == null) return; - - if (ZAConfigFeatures.awareness_Sound_OverworldOnly) { - if (world.provider.getDimension() != 0 && world.provider.getDimension() != -127) return; - } - - if (!canSpawnTrace(world, x, y, z)) { - return; - } - - EntityPlayer closestPlayer = getClosestPlayer(world, x, y, z, 128); - - String soundName = SoundProfileEntry.getSoundEventName(sound); - - double strength = ZAConfig.soundStrength; - - Vec3d pos = new Vec3d(x, y, z); - - if (closestPlayer != null) { - double distToPlayer = closestPlayer.getDistance(x, y, z); - - SoundProfileEntry entry = getFirstEntry(soundName); - - if (entry != null) { - if (distToPlayer <= entry.getDistanceMax()) { - if (entry.getOddsTo1ToUse() <= 0 || rand.nextInt(entry.getOddsTo1ToUse()) == 0) { - strength *= entry.getMultiplier(); - - EntityScent scent = spawnOrBuffSenseAtPos(world, pos, EnumSenseType.SOUND, (int)strength); - - ZombieAwareness.dbg("spawned or buffed sound sense from soundEvent, sound: " + soundName + ", str: " + scent.getStrengthPeak() + ", vol: " + volume); - } - } - } - } - } - - public static void hookBlockEvent(PlayerEvent event, int chance) { - - if (!ZAConfigFeatures.awareness_Sound) return; - - if (ZAConfigFeatures.awareness_Sound_OverworldOnly) { - if (event.getEntity().world.provider.getDimension() != 0 && event.getEntity().world.provider.getDimension() != -127) return; - } - - if (event.getEntityPlayer() == null || (ZAConfigPlayerLists.whiteListUsedSenses && !ZAConfigPlayerLists.whitelistSenses.contains(CoroUtilEntity.getName(event.getEntityPlayer())))) return; - - if (!event.getEntity().world.isRemote && event.getEntity().world.rand.nextInt(chance) == 0) { - - int strength = ZAConfig.soundStrength; - Vec3d pos = new Vec3d(event.getEntityPlayer().posX, event.getEntityPlayer().posY, event.getEntityPlayer().posZ); - - EntityScent scent = spawnOrBuffSenseAtPos(event.getEntity().world, pos, EnumSenseType.SOUND, strength); - - ZombieAwareness.dbg("spawned or buffed sound sense from PlayerEvent: " + scent.getStrengthPeak()); - } - } - - /** - * Player can be null - * - * @param player - * @param chance - */ - public static void handleBlockBasedEvent(EntityPlayer player, World world, BlockPos pos, int chance) { - if (player == null && ZAConfig.blockBreakEvent_PlayersOnly) { - return; - } - - if (!ZAConfigFeatures.awareness_Sound) return; - - if (ZAConfigFeatures.awareness_Sound_OverworldOnly) { - if (world.provider.getDimension() != 0 && world.provider.getDimension() != -127) return; - } - - if (player != null && ZAConfigPlayerLists.whiteListUsedSenses) { - if (ZAConfigPlayerLists.whitelistSenses.contains(CoroUtilEntity.getName(player))) return; - } - - if (!world.isRemote && world.rand.nextInt(chance) == 0) { - - int strength = ZAConfig.soundStrength; - Vec3d posVec = new Vec3d(pos.getX(), pos.getY(), pos.getZ()); - - EntityScent scent = spawnOrBuffSenseAtPos(world, posVec, EnumSenseType.SOUND, strength); - - ZombieAwareness.dbg("spawned or buffed sound sense from BlockBasedEvent: " + scent.getStrengthPeak()); - } - } - - public static void hookSetAttackTarget(LivingSetAttackTargetEvent event) { - - //ZombieAwareness.dbg(event.getEntityLiving().getEntityId() + " targetting " + event.getTarget()); - - if (event.getEntityLiving() instanceof EntityLiving) { - if (event.getTarget() instanceof EntityPlayer) { - //tryPlayAlertSound((EntityLiving)event.getEntityLiving(), new Vec3d(event.getTarget().posX, event.getTarget().posY, event.getTarget().posZ)); - tryPlayTargetSound((EntityLiving)event.getEntityLiving(), (EntityLivingBase)event.getTarget(), new Vec3d(event.getEntityLiving().posX, event.getEntityLiving().posY, event.getEntityLiving().posZ)); - } else if (event.getTarget() == null) { - //dont use, AI stupidly detargets when resetting tasks despite still chasing player, causing double alert noise if this code is used - /*if (lookupLastAlertTime.containsKey(event.getEntityLiving())) { - lookupLastAlertTime.remove(event.getEntityLiving()); - System.out.println("detarget"); - }*/ - } - } - - } - - public static void hookPlayEvent(World world, int type, - BlockPos blockPosIn, int data) { - //if event type is for playing a record - if (world.isRemote) return; - if (type == 1010) { - //if putting in a record and not taking out - if (Item.getItemById(data) instanceof ItemRecord) { - Vec3d pos = new Vec3d(blockPosIn.getX(), blockPosIn.getY(), blockPosIn.getZ()); - EntityScent scent = spawnOrBuffSenseAtPos(world, pos, EnumSenseType.SOUND, 300); - ZombieAwareness.dbg("spawned or buffed sound sense from playEvent: " + scent.getStrengthPeak()); - } - } - } - - public static void spawnNewMobSurface(EntityPlayer player) { - - - int minDist = ZAConfigSpawning.extraSpawningDistMin; - int maxDist = ZAConfigSpawning.extraSpawningDistMax; - int range = maxDist * 2; - - for (int tries = 0; tries < 5; tries++) { - int tryX = (int)Math.floor(player.posX - (range/2) + (rand.nextInt(range))); - int tryZ = (int)Math.floor(player.posZ - (range/2) + (rand.nextInt(range))); - int tryY = player.world.getHeight(new BlockPos(tryX, 0, tryZ)).getY(); - - if (player.getDistance(tryX, tryY, tryZ) < minDist || - player.getDistance(tryX, tryY, tryZ) > maxDist || - !canSpawnMobOnGround(player.world, tryX, tryY - 1, tryZ) || - player.world.getLightFromNeighbors(new BlockPos(tryX, tryY, tryZ)) >= 6) { - continue; - } - - int randSize = player.world.rand.nextInt(ZAConfigSpawning.extraSpawningSurfaceMaxGroupSize) + 1; - WorldServer world = (WorldServer) player.world; - for (int i = 0; i < randSize; i++) { - spawnMobsAllowed(player, world, tryX, tryY, tryZ); - } - - //if (ZAConfigSpawning.extraSpawningAutoTarget) entZ.setAttackTarget(player); - - if (ZAConfig.debugConsoleSpawns) ZombieAwareness.dbg("spawnNewMobSurface: " + tryX + ", " + tryY + ", " + tryZ); - - return; - } - } - - public static void spawnNewMobCave(EntityPlayer player) { - - int minDist = ZAConfigSpawning.extraSpawningCavesDistMin; - int maxDist = ZAConfigSpawning.extraSpawningCavesDistMax; - int range = maxDist * 2; - - for (int tries = 0; tries < ZAConfigSpawning.extraSpawningCavesTryCount; tries++) { - int tryX = (int)Math.floor(player.posX - (range/2) + (rand.nextInt(range))); - int tryY = (int)Math.floor(player.posY - (range/2) + (rand.nextInt(range))); - int tryZ = (int)Math.floor(player.posZ - (range/2) + (rand.nextInt(range))); - - if (player.getDistance(tryX, tryY, tryZ) < minDist || - player.getDistance(tryX, tryY, tryZ) > maxDist || - !canSpawnMobOnGround(player.world, tryX, tryY - 1, tryZ) || - !isInDarkCave(player.world, tryX, tryY, tryZ, true)) { - continue; - } - - int randSize = player.world.rand.nextInt(ZAConfigSpawning.extraSpawningCavesMaxGroupSize) + 1; - WorldServer world = (WorldServer) player.world; - for (int i = 0; i < randSize; i++) { - spawnMobsAllowed(player, world, tryX, tryY, tryZ); - } - - - - return; - } - } - - public static void spawnMobsAllowed(EntityPlayer player, WorldServer world, int tryX, int tryY, int tryZ) { - if (!ZAConfigSpawning.extraSpawningUseNaturalSpawnList) { - EntityZombie entZ = new EntityZombie(world); - entZ.setPosition(tryX + 0.5F, tryY + 1.1F, tryZ + 0.5F); - entZ.onInitialSpawn(world.getDifficultyForLocation(new BlockPos(entZ)), (IEntityLivingData)null); - giveRandomSpeedBoost(entZ); - world.spawnEntity(entZ); - - if (ZAConfigSpawning.extraSpawningAutoTarget) entZ.setAttackTarget(player); - - if (ZAConfig.debugConsoleSpawns) { - ZombieAwareness.dbg("spawnNewMob: " + tryX + ", " + tryY + ", " + tryZ); - } - } else { - SpawnListEntry spawnlistentry = world.getSpawnListEntryForTypeAt(EnumCreatureType.MONSTER, new BlockPos(tryX, tryY, tryZ)); - - EntityLiving entityliving; - - try - { - entityliving = (EntityLiving)spawnlistentry.entityClass.getConstructor(new Class[] {World.class}).newInstance(new Object[] {world}); - entityliving.setLocationAndAngles(tryX + 0.5F, tryY + 1.1F, tryZ + 0.5F, world.rand.nextFloat() * 360.0F, 0.0F); - - Result canSpawn = ForgeEventFactory.canEntitySpawn(entityliving, world, tryX + 0.5F, tryY + 1.1F, tryZ + 0.5F); - if (canSpawn == Result.ALLOW || (canSpawn == Result.DEFAULT && entityliving.getCanSpawnHere())) - { - world.spawnEntity(entityliving); - if (!ForgeEventFactory.doSpecialSpawn(entityliving, world, tryX + 0.5F, tryY + 1.1F, tryZ + 0.5F)) - { - entityliving.onInitialSpawn(world.getDifficultyForLocation(new BlockPos(entityliving)), (IEntityLivingData) null); - } - giveRandomSpeedBoost(entityliving); - if (ZAConfig.debugConsoleSpawns) { - ZombieAwareness.dbg("spawnNewMob: " + tryX + ", " + tryY + ", " + tryZ + ", name: " + entityliving.toString()); - } - - if (ZAConfigSpawning.extraSpawningAutoTarget) entityliving.setAttackTarget(player); - - } - } - catch (Exception exception) - { - exception.printStackTrace(); - } - } - } - - /** - * Method is far from perfect, but should work well enough without intensive processing to verify - * coords fed in should be solid block with air above it (2 blocks of vertical space, 1 width of size) - * - * @param x - * @param y - * @param z - * @return - */ - public static boolean isInDarkCave(World world, int x, int y, int z, boolean checkSpaceToSpawn) { - BlockPos pos = new BlockPos(x, y, z); - IBlockState state = world.getBlockState(pos); - Block block = state.getBlock(); - if (!world.canSeeSky(pos) && world.getLightFromNeighbors(pos) < 5) { - if (!CoroUtilBlock.isAir(block) && state.getMaterial() == Material.ROCK/*(block != Blocks.grass || block.getMaterial() != Material.grass)*/) { - - if (!checkSpaceToSpawn) { - return true; - } else { - Block blockAir1 = world.getBlockState(new BlockPos(x, y+1, z)).getBlock(); - if (CoroUtilBlock.isAir(blockAir1)) { - Block blockAir2 = world.getBlockState(new BlockPos(x, y+2, z)).getBlock(); - if (CoroUtilBlock.isAir(blockAir2)) { - return true; - } - } - - } - } - } - return false; - } - - /** - * x y z coords are expected to be the ground the mob is going to spawn on - * - * @param world - * @param x - * @param y - * @param z - * @return - */ - public static boolean canSpawnMobOnGround(World world, int x, int y, int z) { - BlockPos pos = new BlockPos(x, y, z); - IBlockState state = world.getBlockState(pos); - Block block = state.getBlock();//Block.pressurePlatePlanks.blockID; - - /*if (id == Block.grass.blockID || id == Block.stone.blockID || id == Block.tallGrass.blockID || id == Block.grass.blockID || id == Block.sand.blockID) { - return true; - }*/ - if (CoroUtilBlock.isAir(block) || !block.canCreatureSpawn(state, world, pos, EntityLiving.SpawnPlacementType.ON_GROUND)) { - return false; - } - return true; - } - - public static void spawnWaypoint(Entity entSource) { - - int range = 256; - - double tryX = (int)entSource.posX - (range/2) + (rand.nextInt(range)); - double tryZ = (int)entSource.posZ - (range/2) + (rand.nextInt(range)); - double tryY = entSource.world.getHeight(new BlockPos(tryX, 0, tryZ)).getY(); - - if (!canSpawnTrace(entSource.world, tryX, tryY, tryZ)) { - return; - } - - double height = entSource.posY; - - EntityScent var1 = getSenseNodeAtPos(entSource.world, new Vec3d(tryX, tryY, tryZ), EnumSenseType.WAYPOINT); - - boolean newNode = false; - - if (var1 == null) { - var1 = new EntityScent(entSource.world); - newNode = true; - } - - var1.setStrengthPeak(60); - - if (newNode) { - var1.setPosition(tryX, tryY, tryZ); - var1.type = 2; - - entSource.world.spawnEntity(var1); - } - - if (debug) System.out.println("WP: " + entSource + " - range: " + var1.getRange()); - } - - public static boolean canSpawnTrace(World world, double x, double y, double z) { - BlockPos pos = new BlockPos(x,y,z); - if (!world.isBlockLoaded(pos)) return false; - IBlockState state = world.getBlockState(pos); - //iirc circuits check was to prevent senses spawning on pressure plates and triggering them, but there should be better ways to stop that... - //might be redundant since AABB and canTriggerWalking and canBeCollidedWith fix - if (state.getMaterial() == Material.CIRCUITS && (!(state.getBlock() instanceof BlockButton) && !(state.getBlock() instanceof BlockLever))) { - return false; - } - return true; - } - - public static EntityPlayer getClosestPlayerToEntity(World world, Entity par1Entity, double par2) - { - return getClosestPlayer(world, par1Entity.posX, par1Entity.posY, par1Entity.posZ, par2); - } - - /** - * Gets the closest player to the point within the specified distance (distance can be set to less than 0 to not - * limit the distance). Args: x, y, z, dist - */ - public static EntityPlayer getClosestPlayer(World world, double par1, double par3, double par5, double par7) - { - double d4 = -1.0D; - EntityPlayer entityplayer = null; - - for (int i = 0; i < world.playerEntities.size(); ++i) - { - EntityPlayer entityplayer1 = (EntityPlayer)world.playerEntities.get(i); - if (!ZAConfigPlayerLists.whiteListUsedSenses || ZAConfigPlayerLists.whitelistSenses.contains(CoroUtilEntity.getName(entityplayer1))) { - double d5 = entityplayer1.getDistanceSq(par1, par3, par5); - - if ((par7 < 0.0D || d5 < par7 * par7) && (d4 == -1.0D || d5 < d4)) - { - d4 = d5; - entityplayer = entityplayer1; - } - } - } - - return entityplayer; - } - - /** - * Checks if a scent of the same type is already at this location - * - * @param parWorld - * @param parPos - * @param type - * @return - */ - public static EntityScent getSenseNodeAtPos(World parWorld, Vec3d parPos, EnumSenseType type) { - - if (ZAConfig.extraScentCutoffRange == -1) return null; - - AxisAlignedBB aabb = new AxisAlignedBB(parPos.x, parPos.y, parPos.z, parPos.x + 1, parPos.y + 1, parPos.z + 1); - aabb = aabb.grow(ZAConfig.extraScentCutoffRange, ZAConfig.extraScentCutoffRange, ZAConfig.extraScentCutoffRange); - - List list = parWorld.getEntitiesWithinAABB(EntityScent.class, aabb); - - if (list.size() > 0) { - for(int j = 0; j < list.size(); j++) - { - EntityScent node = (EntityScent)list.get(j); - if (node.type == type.ordinal()) { - return node; - } - } - } - - return null; - } - - public static EntityScent spawnOrBuffSenseAtPos(World world, Vec3d parPos, EnumSenseType type, int strength) { - return spawnOrBuffSenseAtPos(world, parPos, type, strength, true); - } - - /** - * Tries to spawn a new sense, if one is close enough, it will multiply that senses current strength by lastMultiply - * - * @param world - * @param parPos - * @param type - * @param strength - * @return - */ - public static EntityScent spawnOrBuffSenseAtPos(World world, Vec3d parPos, EnumSenseType type, int strength, boolean frequentSoundMultiply) { - - EntityScent sense = getSenseNodeAtPos(world, parPos, type); - - if (sense == null) { - sense = new EntityScent(world); - sense.type = type.ordinal(); - sense.setPosition(parPos.x, parPos.y, parPos.z); - sense.setStrengthPeak(strength); - world.spawnEntity(sense); - } else if (frequentSoundMultiply) { - //instead of amplifying current strength, amp the base value, but only if current strength is weaker than param - float str = sense.getStrengthPeak(); - if (str < strength) { - str = strength; - } - - if(sense.lastBuffTime + (long)ZAConfig.frequentSoundThreshold > System.currentTimeMillis()) { - sense.lastMultiply += 0.1F; - str *= sense.lastMultiply; - } else { - sense.lastMultiply = 1.0F; - } - - sense.lastBuffTime = System.currentTimeMillis(); - sense.setStrengthPeak((int)str); - } - - return sense; - } - - public static void tryPlayTargetSound(EntityLiving entAlerted, EntityLivingBase entTargetted, Vec3d pos) { - - if (!ZAConfigFeatures.soundAlerts) return; - - if (!ZombieAwareness.canProcessEntity(entAlerted)) return; - - //prevent target spam sound from omniscient zombies - if (entAlerted.getEntityData().hasKey(UtilEntityBuffs.dataEntityBuffed_AI_Omniscience)) { - return; - } - - //added max dist and blocks loaded check due to https://github.com/Corosauce/ZombieAwareness/issues/11 - double distMaxCancel = 75; - if (!lookupLastAlertTime.containsKey(entAlerted) || lookupLastAlertTime.get(entAlerted) + alertDelay < entAlerted.world.getTotalWorldTime()) { - if (entAlerted.getDistanceToEntity(entTargetted) < distMaxCancel - && entAlerted.getEntityWorld().provider.getDimension() == entTargetted.getEntityWorld().provider.getDimension() - && entAlerted.getEntityWorld().isBlockLoaded(entAlerted.getPosition()) - && entTargetted.getEntityWorld().isBlockLoaded(entTargetted.getPosition())) { - if (entAlerted.canEntityBeSeen(entTargetted)) { - //entAlerted.world.playSound(null, pos.x, pos.y, pos.z, SoundRegistry.get("target"), SoundCategory.HOSTILE, 3F, 0.8F + (entAlerted.world.rand.nextFloat() * 0.2F)); - entAlerted.world.playSound(null, entTargetted.posX, entTargetted.posY, entTargetted.posZ, ZAConfigFeatures.soundUseAlternateAlertNoise ? SoundRegistry.get("alert") : SoundRegistry.get("target"), SoundCategory.HOSTILE, (float) ZAConfigFeatures.soundVolumeAlertTarget, ZAConfigFeatures.soundUseAlternateAlertNoise ? 1F : 0.8F + (entAlerted.world.rand.nextFloat() * 0.2F)); - lookupLastAlertTime.put(entAlerted, entAlerted.world.getTotalWorldTime()); - //ZombieAwareness.dbg("!!! alert play for ent: " + entAlerted.getEntityId() + ", lookupSize: " + lookupLastAlertTime.size()); - } else { - //likely due to new call for help routine in vanilla, so treat it like investigating until line of sight is made - tryPlayInvestigateSound(entAlerted, pos); - //ZombieAwareness.dbg("??? tried play alert for no LOS entity: " + entAlerted.getEntityId() + ", lookupSize: " + lookupLastAlertTime.size()); - } - } - } else { - //ZombieAwareness.dbg("already played alert for ent: " + entAlerted.getEntityId() + ", lookupSize: " + lookupLastAlertTime.size()); - } - } - - public static void tryPlayInvestigateSound(EntityLiving entAlerted, Vec3d pos) { - - if (!ZAConfigFeatures.soundInvestigates) return; - - if (!ZombieAwareness.canProcessEntity(entAlerted)) return; - - if (!lookupLastInvestigateTime.containsKey(entAlerted) || lookupLastInvestigateTime.get(entAlerted) + investigateDelay < entAlerted.world.getTotalWorldTime()) { - entAlerted.world.playSound(null, pos.x, pos.y, pos.z, SoundRegistry.get("investigate"), SoundCategory.HOSTILE, (float)ZAConfigFeatures.soundVolumeInvestigate, 0.7F + (entAlerted.world.rand.nextFloat() * 0.3F)); - lookupLastInvestigateTime.put(entAlerted, entAlerted.world.getTotalWorldTime()); - //ZombieAwareness.dbg("!!! investigate play for ent: " + entAlerted.getEntityId() + ", lookupSize: " + lookupLastInvestigateTime.size()); - } - } - - public static boolean isZombieAwarenessActive(World world) { - if (world == null) return false; - if (ZAConfig.daysBeforeFeaturesActivate <= 0) return true; - if (((double)world.getWorldTime() / CoroUtilWorldTime.getDayLength()) >= ZAConfig.daysBeforeFeaturesActivate) { - return true; - } else { - return false; - } - } - - public static WorldData getWorldData(int dimID) { - if (!lookupWorldData.containsKey(dimID)) { - lookupWorldData.put(dimID, new WorldData()); - } - return lookupWorldData.get(dimID); - } -} diff --git a/src/main/java/ZombieAwareness/ZombieAwareness.java b/src/main/java/ZombieAwareness/ZombieAwareness.java deleted file mode 100644 index f74c5da..0000000 --- a/src/main/java/ZombieAwareness/ZombieAwareness.java +++ /dev/null @@ -1,428 +0,0 @@ -package ZombieAwareness; - -import java.io.File; -import java.util.*; - -import CoroUtil.forge.CULog; -import ZombieAwareness.config.*; -import modconfig.ConfigMod; -import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; -import net.minecraft.command.ServerCommandManager; -import net.minecraft.entity.*; -import net.minecraft.entity.monster.*; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.Blocks; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.world.World; -import net.minecraftforge.common.DimensionManager; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.common.config.Configuration; -import net.minecraftforge.fml.common.FMLCommonHandler; -import net.minecraftforge.fml.common.Mod; -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 CoroUtil.OldUtil; -import CoroUtil.pathfinding.IPFCallback; -import CoroUtil.pathfinding.PFCallbackItem; -import CoroUtil.util.CoroUtilBlock; -import CoroUtil.util.CoroUtilEntity; -import net.minecraftforge.fml.common.registry.EntityEntry; -import net.minecraftforge.fml.common.registry.ForgeRegistries; - -@Mod(modid = ZombieAwareness.modID, name="Zombie Awareness", version=ZombieAwareness.version, dependencies="required-after:coroutil") -public class ZombieAwareness implements IPFCallback { - - @Mod.Instance( value = ZombieAwareness.modID ) - public static ZombieAwareness instance; - public static final String modID = "zombieawareness"; - public static final String version = "${version}"; - - @SidedProxy(clientSide = "ZombieAwareness.ClientProxy", serverSide = "ZombieAwareness.CommonProxy") - public static CommonProxy proxy; - - public static Configuration config; - public static String configCategory = "tickable_entities"; - - //used mainly for filename placeholder for dynamic config - public static ZAConfigMobLists mobLists = new ZAConfigMobLists(); - - public static HashMap lookupClassToOldName = new HashMap<>(); - public static HashMap lookupClassToRegisteredName = new HashMap<>(); - - @Mod.EventHandler - public void preInit(FMLPreInitializationEvent event) - { - ZAConfigFeatures configMain = new ZAConfigFeatures(); - ConfigMod.addConfigFile(event, new ZAConfig()); - ConfigMod.addConfigFile(event, configMain); - ConfigMod.addConfigFile(event, new ZAConfigPlayerLists()); - ConfigMod.addConfigFile(event, new ZAConfigSpawning()); - ConfigMod.addConfigFile(event, new ZAConfigClient()); - - - //sync to forge values - configMain.hookUpdatedValues(); - - config = new Configuration(new File("config" + File.separator + mobLists.getConfigFileName() + ".cfg")); - String comment = "Entities to be enhanced, will show everything ZombieAwareness should be able to enhance, " + - "only zombie based mobs and some others are default on, if a mob uses ground pathfinding and is able to be hostile you can try enabling more from other mods, " + - "but how well the enhancing works depends on how the mod implemented their mob"; - config.setCategoryComment(configCategory, comment); - config.save(); - - SoundRegistry.init(); - } - - @Mod.EventHandler - public void load(FMLInitializationEvent event) - { - MinecraftForge.EVENT_BUS.register(new ZAEventHandler()); - - proxy.init(this); - } - - @Mod.EventHandler - public void loadPost(FMLPostInitializationEvent event) { - //TODO: move?? - try { - generateEntityTickList(); - } catch (Exception ex) { - ex.printStackTrace(); - } - - } - - - @Mod.EventHandler - public void serverStarting(FMLServerStartingEvent event) { - ((ServerCommandManager)FMLCommonHandler.instance().getMinecraftServerInstance().getCommandManager()).registerCommand(new CommandZA()); - } - - public ZombieAwareness() { - - } - - public void onTick() { - onTickInGame(); - } - - public void onTickInGame() { - - World world = DimensionManager.getWorld(0); - - if (world != null) { - tickOverworld(world); - } - - - - } - - public static void tickWorld(World world) { - - int lastCountZombies = 0; - int lastCountMobsSurface = 0; - int lastCountMobsCaves = 0; - - int dimID = world.provider.getDimension(); - - //calculate entity counts needed for some features - - if (world.getTotalWorldTime() % 40 == 0) { - for (int i = 0; i < world.loadedEntityList.size(); i++) { - Entity ent = world.loadedEntityList.get(i); - - int x = MathHelper.floor(ent.posX); - int y = MathHelper.floor(ent.posY); - int z = MathHelper.floor(ent.posZ); - - if (ent instanceof EntityZombie || (ZAConfigSpawning.extraSpawningUseNaturalSpawnList && ent instanceof IMob)) { - if (ent instanceof EntityZombie) { - lastCountZombies++; - } - - //not subtracting 0.5 incase of slab - if (ZAUtil.isInDarkCave(world, x, (int)(ent.getEntityBoundingBox().minY - 0.3D), z, false)) { - lastCountMobsCaves++; - } else { - lastCountMobsSurface++; - } - } - - } - - /*CULog.dbg("lastCountZombies: " + lastCountZombies + ", dimID: " + dimID); - CULog.dbg("lastCountMobsSurface: " + lastCountMobsSurface); - CULog.dbg("lastCountMobsCaves: " + lastCountMobsCaves);*/ - - ZAUtil.getWorldData(dimID).lastZombieCount = lastCountZombies; - ZAUtil.getWorldData(dimID).lastMobsCountSurface = lastCountMobsSurface; - ZAUtil.getWorldData(dimID).lastMobsCountCaves = lastCountMobsCaves; - } - } - - public void tickOverworld(World world) { - if (world.isRemote) return; - - manageCallbackQueue(); - - ZAUtil.trackProfile(); - } - - public static void tickEntity(EntityLivingBase ent) { - - boolean spawned = false; - - Random rand = new Random(); - - World world = ent.world; - int dimID = world.provider.getDimension(); - - //stagger ticking by entity id - if ((world.getTotalWorldTime() + ent.getEntityId()) % ZAConfig.tickRateAILoop == 0) { - if (canProcessEntity(ent) && ent instanceof EntityLiving) { - - ZAUtil.tickAI((EntityLiving)ent); - - if ((dimID == 0) && ent instanceof IMob) { - - int x = MathHelper.floor(ent.posX); - int y = MathHelper.floor(ent.posY); - int z = MathHelper.floor(ent.posZ); - - if (ent instanceof EntityZombie) { - if (ZAUtil.getWorldData(dimID).lastSpawnTime < ent.world.getTotalWorldTime() && !spawned && ent.world.getClosestPlayerToEntity(ent, 32) == null && rand.nextInt(ZAConfigSpawning.maxZombiesNightBaseRarity + (ZAUtil.getWorldData(dimID).lastZombieCount * 4 / (Math.max(1, ZAConfig.tickRateAILoop)))) == 0) { - if (!ent.world.isDaytime() && ZAUtil.getWorldData(dimID).lastZombieCount < ZAConfigSpawning.maxZombiesNight && ent.world.canSeeSky(new BlockPos(x, y, z)) && ent.world.getLightFromNeighbors(new BlockPos(x, y, z)) < 5) { - - CULog.dbg("spawning extra zombie clone, dim " + dimID + ", last count: " + ZAUtil.getWorldData(dimID).lastZombieCount); - - EntityZombie entZ = new EntityZombie(ent.world); - entZ.setPosition(ent.posX, ent.posY, ent.posZ); - entZ.onInitialSpawn(world.getDifficultyForLocation(new BlockPos(entZ)), (IEntityLivingData)null); - ZAUtil.giveRandomSpeedBoost(entZ); - ent.world.spawnEntity(entZ); - //lastZombieCount = ++lastCount; - - spawned = true; - ZAUtil.getWorldData(dimID).lastSpawnTime = ZAConfigSpawning.zombieSpawnTickDelay + ent.world.getTotalWorldTime(); - ZAUtil.getWorldData(dimID).lastSpawnSysTime = System.currentTimeMillis(); - - if (ZAConfig.debugConsoleSpawns) dbg("Spawned new surface zombie at: " + ent.posX + ", " + ent.posY + ", " + ent.posZ); - } else if (ZAConfigFeatures.extraSpawningCave && ZAUtil.getWorldData(dimID).lastZombieCount < ZAConfigSpawning.maxZombiesNight/*lastZombieCountCaves < ZAConfigSpawning.extraSpawningCavesMaxCount*/) { - EntityPlayer closestPlayer = ent.world.getNearestAttackablePlayer(ent, ZAConfigSpawning.extraSpawningDistMax, ZAConfigSpawning.extraSpawningDistMax); - if (closestPlayer != null && (!ZAConfigPlayerLists.whiteListUsedExtraSpawning || ZAConfigPlayerLists.whitelistExtraSpawning.contains(CoroUtilEntity.getName(closestPlayer))) - && closestPlayer.getDistanceSqToEntity(ent) > ZAConfigSpawning.extraSpawningDistMin - && !ent.world.canSeeSky(new BlockPos(x, y, z)) && ent.world.getLightFromNeighbors(new BlockPos(x, y, z)) < 5) { - - IBlockState state = ent.world.getBlockState(new BlockPos(x, (int)(ent.getEntityBoundingBox().minY - 0.5D), z)); - - if (!CoroUtilBlock.isAir(state.getBlock()) && (state.getBlock() != Blocks.GRASS || state.getMaterial() == Material.GRASS)) { - EntityZombie entZ = new EntityZombie(ent.world); - entZ.setPosition(ent.posX, ent.posY, ent.posZ); - entZ.onInitialSpawn(world.getDifficultyForLocation(new BlockPos(entZ)), (IEntityLivingData)null); - ZAUtil.giveRandomSpeedBoost(entZ); - ent.world.spawnEntity(entZ); - - if (ZAConfigSpawning.extraSpawningAutoTarget) entZ.setAttackTarget(closestPlayer); - - //lastZombieCount = ++lastCount; - - spawned = true; - ZAUtil.getWorldData(dimID).lastSpawnTime = ZAConfigSpawning.zombieSpawnTickDelay + ent.world.getTotalWorldTime(); - ZAUtil.getWorldData(dimID).lastSpawnSysTime = System.currentTimeMillis(); - - if (ZAConfig.debugConsoleSpawns) dbg("Spawned new cave zombie at: " + ent.posX + ", " + ent.posY + ", " + ent.posZ); - } - } - } - } - } - - } - } - } - } - - public static boolean toggle = false; - - public ArrayList callbackList = new ArrayList(); - - @Override - public synchronized void pfComplete(PFCallbackItem ci) { - //System.out.println("callback: " + ci.ent); - callbackList.add(ci); - } - - @Override - public void manageCallbackQueue() { - ArrayList list = getQueue(); - - try { - for (int i = 0; i < list.size(); i++) { - PFCallbackItem item = list.get(i); - - if (!item.ent.isDead && OldUtil.chunkExists(item.ent.world, MathHelper.floor(item.ent.posX / 16), MathHelper.floor(item.ent.posZ / 16))) { - item.ent.getNavigator().setPath(item.pe, item.speed); - } - } - } catch (Exception ex) { - System.out.println("Crash in ZA Callback Item manager"); - ex.printStackTrace(); - } - - //if (list.size() > 0) System.out.println("cur list size: " + list.size()); - - list.clear(); - } - - @Override - public ArrayList getQueue() { - return callbackList; - } - - public static boolean canProcessEntity(Entity ent) { - if (!canEntityBeProcessedOverride(ent)) { - return false; - } - return canProcessEntity(ent.getClass(), false); - } - - /** - * Looks up and generates config info if entry missing - * - * @param ent - * @param pregen - * @return - */ - public static boolean canProcessEntity(Class ent, boolean pregen) { - - String entName = getEntityRegisteredName(ent); - if (ZAUtil.lookupTickableEntities.containsKey(ent)) - { - return ZAUtil.lookupTickableEntities.get(ent); - } - - boolean result = false; - if (canConfigEntity(ent)) { - if (!pregen) config.load(); - boolean canProcess = getDefaultForEntity(ent); - try { - //prevent crash for case where mod entity can be null or blank - if (entName != null && !entName.equals("")) { - result = config.get(configCategory, entName, canProcess).getBoolean(canProcess); - } - } catch (Exception ex) { - ex.printStackTrace(); - } - if (!pregen) config.save(); - ZAUtil.lookupTickableEntities.put(ent, result); - } - - return result; - } - - /** - * Handles special cases for specific instances like if owned and has an owner. But I want that to never be processed anyways - * Placeholder for now until more dynamic needs are found - * - * @param entity - * @return false if you want to cancel processing, true lets it continue with other rules - */ - public static boolean canEntityBeProcessedOverride(Entity entity) { - return true; - /*boolean result = false; - if (entity instanceof IEntityOwnable) { - if (entity. - }*/ - } - - /** - * Used to avoid adding entries to config that cant be used even if set to true - * - * @param ent - * @return - */ - public static boolean canConfigEntity(Class ent) { - return EntityMob.class.isAssignableFrom(ent) || IMob.class.isAssignableFrom(ent); - } - - public static boolean getDefaultForEntity(Class ent) { - - boolean result = false; - if (canConfigEntity(ent)) { - - if (EntityPigZombie.class.isAssignableFrom(ent)) return false; - - if (EntityZombie.class.isAssignableFrom(ent) || - AbstractSkeleton.class.isAssignableFrom(ent) || - EntityWitch.class.isAssignableFrom(ent) || - EntitySpider.class.isAssignableFrom(ent)) { - result = true; - } else { - result = false; - } - } - return result; - } - - /** - * Generates list of entities we can process, these are written to config they can modify every entities config after first run - * - */ - public static void generateEntityTickList() { - config.load(); - - for (Map.Entry entry : ForgeRegistries.ENTITIES.getEntries()) { - - lookupClassToOldName.put(entry.getValue().getEntityClass(), entry.getValue().getName()); - lookupClassToRegisteredName.put(entry.getValue().getEntityClass(), entry.getKey().toString()); - - boolean tickEnt = canProcessEntity(entry.getValue().getEntityClass(), true); - } - config.save(); - } - - public static void dbg(Object obj) { - if (ZAConfig.debugConsole) { - System.out.println(obj); - } - } - - /** - * 1.10.2: only name available - * 1.11.2: 'name' field in EntityEntry, new registered name is the snake case key for ForgeRegistries.ENTITIES.getEntries() as a resource location - * - * @param ent - * @return - */ - public static String getEntityRegisteredName(Class ent) { - try { - - return getClassToOldName(ent); - } catch (Exception ex) { - if (ZAConfig.debugConsole) { - ex.printStackTrace(); - } - return ent.getClass().getSimpleName(); - - } - } - - /** - * Patch method from 1.11.2 entity registry rework, using new cached lookup - * - * @param ent - * @return - */ - public static String getClassToOldName(Class ent) { - - return lookupClassToOldName.get(ent); - - } -} diff --git a/src/main/java/com/corosus/zombieawareness/EntityRegistry.java b/src/main/java/com/corosus/zombieawareness/EntityRegistry.java new file mode 100644 index 0000000..53c41fd --- /dev/null +++ b/src/main/java/com/corosus/zombieawareness/EntityRegistry.java @@ -0,0 +1,30 @@ +package com.corosus.zombieawareness; + +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.entity.EntityType; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.ObjectHolder; + +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) +public class EntityRegistry { + + @ObjectHolder(ZombieAwareness.MODID + ":scent") + public static EntityType SCENT; + + @SubscribeEvent + public static void registerEntity(RegistryEvent.Register> e) { + IForgeRegistry> r = e.getRegistry(); + r.register( + EntityType.Builder.of(EntityScent::new, MobCategory.MISC) + .setShouldReceiveVelocityUpdates(false) + .setUpdateInterval(20) + .setTrackingRange(128) + .sized(0f, 0f) + .build("scent") + .setRegistryName("scent")); + } + +} diff --git a/src/main/java/com/corosus/zombieawareness/EntityScent.java b/src/main/java/com/corosus/zombieawareness/EntityScent.java new file mode 100644 index 0000000..58a30cd --- /dev/null +++ b/src/main/java/com/corosus/zombieawareness/EntityScent.java @@ -0,0 +1,168 @@ +package com.corosus.zombieawareness; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.world.level.Level; +import com.corosus.zombieawareness.config.ZAConfig; +import com.corosus.zombieawareness.config.ZAConfigClient; +import net.minecraftforge.entity.IEntityAdditionalSpawnData; +import net.minecraftforge.network.NetworkHooks; + +public class EntityScent extends Entity implements IEntityAdditionalSpawnData { + + /** + * + * age has a static max that counts down + * + * peak strength is for scaling actual current strength based on age scale + * + */ + + //0 == blood node, 1 == sound node, 2 == wander node + public int type = 0; + public boolean isUsed = false; + + private static final EntityDataAccessor STRENGTH_PEAK = SynchedEntityData.defineId(EntityScent.class, EntityDataSerializers.INT); + private static final EntityDataAccessor AGE = SynchedEntityData.defineId(EntityScent.class, EntityDataSerializers.INT); + + public long lastBuffTime = 0; + public float lastMultiply = 1F; + + public static int MAX_AGE = 30*20; + + public EntityScent(EntityType entityScentEntityType, Level var1) { + super(entityScentEntityType, var1); + } + + @Override + protected void defineSynchedData() { + this.getEntityData().define(STRENGTH_PEAK, Integer.valueOf(0)); + this.getEntityData().define(AGE, Integer.valueOf(0)); + } + + @Override + public boolean canBeCollidedWith() { + return false; + } + + @Override + public boolean isSteppingCarefully() { + return true; + } + + @Override + public boolean shouldRender(double p_145770_1_, double p_145770_3_, double p_145770_5_) { + return true; + } + + @Override + public boolean shouldRenderAtSqrDistance(double p_70112_1_) { + return true; + } + + public float getRange() { + float range = (float)getStrengthScaled() / 100.0F * (float)ZAConfig.maxPFRangeSense; + + return range; + } + + public void setStrengthPeak(int strength) { + int strTry = strength; + if (strTry > ZAConfig.senseMaxStrength) { + strTry = ZAConfig.senseMaxStrength; + } + this.getEntityData().set(STRENGTH_PEAK, strTry); + this.resetAge(); + } + + public void resetAge() { + this.getEntityData().set(AGE, MAX_AGE); + } + + public int getStrengthPeak() { + return this.getEntityData().get(STRENGTH_PEAK); + } + + public int getStrengthScaled() { + return (int)((double)this.getEntityData().get(this.STRENGTH_PEAK) * getAgeScale()); + } + + public double getAgeScale() { + return (double)this.getEntityData().get(this.AGE) / (double)MAX_AGE; + } + + @Override + public void tick() { + + //TODO: if raining, age smell sense much faster + + int age = this.getEntityData().get(AGE); + int decayRate = level.isRaining() && level.canSeeSky(blockPosition()) ? 3 : 1; + age -= decayRate; + if (age < 0) age = 0; + + this.getEntityData().set(AGE, age); + + if(!level.isClientSide() && age <= 0) { + this.kill(); + } + + boolean scentDebug = ZAConfigClient.client_debugSensesVisual; + if (scentDebug) { + if (level.isClientSide()) { + if (level.getGameTime()/*+this.getEntityId()*/ % 5 == 0) { + for (int i = 0; i < getStrengthScaled() / 10; i++) { + double range = 1D; + double x = getX() - level.random.nextDouble() / 2 + level.random.nextDouble(); + double y = getY() - level.random.nextDouble() / 2 + level.random.nextDouble(); + double z = getZ() - level.random.nextDouble() / 2 + level.random.nextDouble(); + if (type == 0) { + level.addParticle(ParticleTypes.HEART, true, x, y, z, 0, 0, 0); + } else if (type == 1) { + level.addParticle(ParticleTypes.NOTE, true, x, y, z, 0, 0, 0); + } else if (type == 2) { + level.addParticle(ParticleTypes.ANGRY_VILLAGER, true, x, y, z, 0, 0, 0); + } + + } + } + } + } + } + + @Override + protected void addAdditionalSaveData(CompoundTag var1) { + var1.putInt("age", this.getEntityData().get(AGE)); + var1.putInt("strengthpeak", this.getEntityData().get(STRENGTH_PEAK)); + var1.putInt("type", type); + } + + @Override + protected void readAdditionalSaveData(CompoundTag var1) { + this.getEntityData().set(AGE, var1.getInt("age")); + this.getEntityData().set(STRENGTH_PEAK, var1.getInt("strengthpeak")); + type = var1.getInt("type"); + } + + @Override + public void writeSpawnData(FriendlyByteBuf data) { + data.writeInt(this.type); + } + + @Override + public void readSpawnData(FriendlyByteBuf data) { + type = data.readInt(); + } + + @Override + public Packet getAddEntityPacket() { + return NetworkHooks.getEntitySpawningPacket(this); + } +} diff --git a/src/main/java/ZombieAwareness/EnumSenseType.java b/src/main/java/com/corosus/zombieawareness/EnumSenseType.java similarity index 92% rename from src/main/java/ZombieAwareness/EnumSenseType.java rename to src/main/java/com/corosus/zombieawareness/EnumSenseType.java index 29ed1c9..2d92e29 100644 --- a/src/main/java/ZombieAwareness/EnumSenseType.java +++ b/src/main/java/com/corosus/zombieawareness/EnumSenseType.java @@ -1,4 +1,4 @@ -package ZombieAwareness; +package com.corosus.zombieawareness; import java.util.EnumSet; import java.util.HashMap; diff --git a/src/main/java/com/corosus/zombieawareness/EventRegistry.java b/src/main/java/com/corosus/zombieawareness/EventRegistry.java new file mode 100644 index 0000000..d68f0e6 --- /dev/null +++ b/src/main/java/com/corosus/zombieawareness/EventRegistry.java @@ -0,0 +1,4 @@ +package com.corosus.zombieawareness; + +public class EventRegistry { +} diff --git a/src/main/java/com/corosus/zombieawareness/ZAEventHandler.java b/src/main/java/com/corosus/zombieawareness/ZAEventHandler.java new file mode 100644 index 0000000..d67bc97 --- /dev/null +++ b/src/main/java/com/corosus/zombieawareness/ZAEventHandler.java @@ -0,0 +1,188 @@ +package com.corosus.zombieawareness; + +import com.corosus.zombieawareness.client.SoundProfileEntry; +import com.mojang.math.Vector3d; +import net.minecraft.world.entity.player.Player; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.event.entity.living.LivingSpawnEvent; +import net.minecraftforge.event.world.ExplosionEvent; +import net.minecraftforge.event.world.NoteBlockEvent; +import com.corosus.zombieawareness.config.ZAConfig; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.level.Level; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.event.entity.PlaySoundAtEntityEvent; +import net.minecraftforge.event.entity.living.LivingEvent; +import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent; +import net.minecraftforge.event.entity.player.PlayerEvent.BreakSpeed; +import net.minecraftforge.event.entity.player.PlayerEvent.HarvestCheck; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod.EventBusSubscriber(modid = ZombieAwareness.MODID, bus = Mod.EventBusSubscriber.Bus.FORGE) +public class ZAEventHandler { + + @SubscribeEvent + public void noteBlockEvent(NoteBlockEvent.Play event) { + if (event.getWorld() instanceof Level) { + ZAUtil.hookSoundEvent(SoundEvents.NOTE_BLOCK_BASS, (Level) event.getWorld(), event.getPos().getX(), event.getPos().getY(), event.getPos().getZ(), 1, 1); + } + } + + @SubscribeEvent + public void soundEvent(PlaySoundAtEntityEvent event) { + + try { + + /*if (event.getSound().getSoundName().toString().contains("piston")) { + System.out.println(event.getSound().getSoundName().toString()); + }*/ + /*String str = event.getSound().getRegistryName().toString(); + if (str.contains("tripwire")) { + System.out.println(str); + }*/ + + if (event.getEntity() != null && !event.getEntity().level.isClientSide()) { + ZAUtil.hookSoundEvent(event.getSound(), event.getEntity().level, event.getEntity().getX(), event.getEntity().getY(), event.getEntity().getZ(), event.getVolume(), event.getPitch()); + //moved to world event listener for getting coords + //ZAUtil.soundHook(event.getSound().getSoundName().toString(), event.getEntity().world, (float)event.getEntity().posX, (float)event.getEntity().posY, (float)event.getEntity().posZ, event.getVolume(), event.getPitch()); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + @SubscribeEvent + public void setAttackTarget(LivingSetAttackTargetEvent event) { + if (!event.getEntityLiving().level.isClientSide) { + if (!ZAUtil.isZombieAwarenessActive(event.getEntityLiving().level)) return; + ZAUtil.hookSetAttackTarget(event); + } + } + + @SubscribeEvent + public void breakSpeed(BreakSpeed event) { + if (!event.getEntityLiving().level.isClientSide) { + if (!ZAUtil.isZombieAwarenessActive(event.getEntityLiving().level)) return; + if (!ZAConfig.blockHittingEvent_Active) return; + //ZombieAwareness.dbg("BreakSpeed event"); + ZAUtil.hookBlockEvent(event, ZAConfig.blockHittingEvent_OddsTo1); + } + } + + @SubscribeEvent + public void harvest(HarvestCheck event) { + if (!event.getEntityLiving().level.isClientSide) { + /*if (!ZAUtil.isZombieAwarenessActive(event.getEntityLiving().level)) return; + ZombieAwareness.dbg("HarvestCheck event"); + ZAUtil.hookBlockEvent(event, 3);*/ + } + } + + /*@SubscribeEvent + public void breakBlock(BlockEvent.HarvestDropsEvent event) { + if (!event.getWorld().isClientSide) { + if (!ZAUtil.isZombieAwarenessActive(event.getWorld())) return; + if (!ZAConfig.blockBreakEvent_Active) return; + ZombieAwarenessOld.dbg("HarvestDrops event"); + ZAUtil.handleBlockBasedEvent(event.getHarvester(), event.getWorld(), event.getPos(), 3); + } + }*/ + + @SubscribeEvent + public void breakBlock(BlockEvent.BreakEvent event) { + if (!event.getWorld().isClientSide() && event.getWorld() instanceof Level) { + if (!ZAUtil.isZombieAwarenessActive((Level)event.getWorld())) return; + if (!ZAConfig.blockBreakEvent_Active) return; + ZombieAwareness.dbg("HarvestDrops event"); + ZAUtil.handleBlockBasedEvent(event.getPlayer(), (Level)event.getWorld(), event.getPos(), 3); + } + } + + @SubscribeEvent + public void interact(PlayerInteractEvent event) { + if (!event.getEntityLiving().level.isClientSide) { + if (event.getHand() == InteractionHand.MAIN_HAND) { + /** + * event is way too spammy, since i have much greater sound play access now I am going to try and avoid using this event entirely + */ + //ZombieAwareness.dbg("interact event"); + //ZAUtil.hookBlockEvent(event, 3); + } + } + } + + /*@SubscribeEvent + public void tickServer(TickEvent.ServerTickEvent event) { + + if (event.phase == TickEvent.Phase.START) { + + if (!ZAUtil.isZombieAwarenessActive(DimensionManager.getWorld(0))) return; + + ZombieAwarenessOld.instance.onTick(); + } + }*/ + + /*@SubscribeEvent + public void worldLoad(WorldEvent.Load event) { + int dimID = event.getWorld().provider.getDimension(); + CULog.dbg("adding ZA world listener for dimID: " + dimID + ", remote?: " + event.getWorld().isClientSide); + event.getWorld().addEventListener(new WorldEventListener(dimID)); + }*/ + + @SubscribeEvent + public void tickEntity(LivingEvent.LivingUpdateEvent event) { + LivingEntity ent = event.getEntityLiving(); + if (ent.level.isClientSide) return; + + //ZombieAwarenessOld.tickEntity(ent); + if ((ent.level.getGameTime() + ent.getId()) % ZAConfig.tickRateAILoop == 0) { + if (ZombieAwareness.canProcessEntity(ent) && ent instanceof Mob) { + ZAUtil.tickAI((Mob) ent); + } + } + } + + @SubscribeEvent + public void spawnEntity(LivingSpawnEvent.SpecialSpawn event) { + LivingEntity ent = event.getEntityLiving(); + if (ent.level.isClientSide) return; + + ZAUtil.processMobSpawn(event); + } + + @SubscribeEvent + public void tickPlayer(TickEvent.PlayerTickEvent event) { + if (event.player.level.isClientSide || event.phase == TickEvent.Phase.END) return; + + if (event.player.level.getGameTime() % ZAConfig.tickRatePlayerLoop == 0) { + + ZAUtil.tickPlayer(event.player); + } + + } + + /*@SubscribeEvent + public void tickWorld(TickEvent.WorldTickEvent event) { + if (event.world.isRemote || event.phase == TickEvent.Phase.END) return; + + ZombieAwarenessOld.tickWorld(event.world); + }*/ + + @SubscribeEvent + public void explosion(ExplosionEvent.Detonate event) { + SoundProfileEntry entry = ZAUtil.getSoundIDEntry(SoundEvents.GENERIC_EXPLODE.location.toString()); + if (entry != null) { + Vec3 pos = event.getExplosion().getPosition(); + Player closestPlayer = ZAUtil.getClosestPlayer(event.getWorld(), pos.x, pos.y, pos.z, 128); + if (closestPlayer != null) { + ZAUtil.handleSoundProfileEvent(event.getWorld(), entry, new Vector3d(pos.x, pos.y, pos.z), closestPlayer); + } + } + } +} diff --git a/src/main/java/com/corosus/zombieawareness/ZAUtil.java b/src/main/java/com/corosus/zombieawareness/ZAUtil.java new file mode 100644 index 0000000..315fb84 --- /dev/null +++ b/src/main/java/com/corosus/zombieawareness/ZAUtil.java @@ -0,0 +1,842 @@ +package com.corosus.zombieawareness; + +import com.corosus.coroutil.util.CoroUtilAttributes; +import com.corosus.coroutil.util.CoroUtilEntity; +import com.corosus.coroutil.util.CoroUtilPath; +import com.corosus.coroutil.util.CoroUtilWorldTime; +import com.corosus.zombieawareness.client.SoundProfileEntry; +import com.corosus.zombieawareness.client.SoundRegistry; +import com.corosus.zombieawareness.config.ZAConfig; +import com.corosus.zombieawareness.config.ZAConfigFeatures; +import com.corosus.zombieawareness.config.ZAConfigPlayerLists; +import com.mojang.math.Vector3d; +import net.minecraft.core.BlockPos; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ai.attributes.AttributeModifier; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.monster.Skeleton; +import net.minecraft.world.entity.monster.Spider; +import net.minecraft.world.entity.monster.Zombie; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.ButtonBlock; +import net.minecraft.world.level.block.LeverBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.pathfinder.Node; +import net.minecraft.world.phys.AABB; +import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent; +import net.minecraftforge.event.entity.living.LivingSpawnEvent; +import net.minecraftforge.event.entity.player.PlayerEvent; + +import java.util.*; + +public class ZAUtil { + + public static Random rand = new Random(); + + public static HashMap lastHealths = new HashMap(); + public static HashMap lastBleedTimes = new HashMap(); + + public static List listSoundProfiles = new ArrayList<>(); + + //runtime cache lookups, populates as sounds are made, useful for partial matches that require iterating entire sound profile list each time + public static HashMap lookupSoundNameToProfileCache = new HashMap<>(); + public static HashMap lookupSoundIntToProfile = new HashMap<>(); + + public static WeakHashMap lookupLastAlertTime = new WeakHashMap<>(); + public static long alertDelay = 60*20; + + public static WeakHashMap lookupLastInvestigateTime = new WeakHashMap<>(); + public static long investigateDelay = 60*20; + + public static HashMap lookupLastWaypointTime = new HashMap<>(); + + public static HashMap lookupTickableEntitiesCache = new HashMap<>(); + + public static String SPEED_BOOST_TAG = "za_speedbosted"; + public static String ZA_LAST_ACTION = "za_last_action"; + + public static boolean debug = false; + + static { + addSoundHooks(); + } + + public static void addSoundHooks() { + int noisyInteractRange = 30; + double noisyInteractBuff = 1.3D; + + listSoundProfiles.clear(); + lookupSoundIntToProfile.clear(); + lookupSoundNameToProfileCache.clear(); + + //short dist ones + listSoundProfiles.add(new SoundProfileEntry(SoundEvents.ARROW_HIT_PLAYER, 1.1D)); + listSoundProfiles.add(new SoundProfileEntry(SoundEvents.ARROW_HIT, 1.1D)); + listSoundProfiles.add(new SoundProfileEntry(SoundEvents.CROSSBOW_HIT, 1.1D)); + + listSoundProfiles.add(new SoundProfileEntry(SoundEvents.CHEST_CLOSE, noisyInteractBuff).setMaxDistToSpawnFromPlayer(noisyInteractRange)); + + List listDoorSoundEvents = new ArrayList<>(); + //trap doors opening and close, metal and wood + listDoorSoundEvents.add(1037); + listDoorSoundEvents.add(1007); + listDoorSoundEvents.add(1036); + listDoorSoundEvents.add(1013); + //doors, opening and close, metal and wood + listDoorSoundEvents.add(1011); + listDoorSoundEvents.add(1012); + listDoorSoundEvents.add(1005); + listDoorSoundEvents.add(1006); + //fence gates + listDoorSoundEvents.add(1008); + listDoorSoundEvents.add(1014); + addSoundIntegerEntry(new SoundProfileEntry("debug-doors", noisyInteractBuff).setListSoundEventTypes(listDoorSoundEvents)); + + //records + List list = new ArrayList<>(); + list.add(1010); + addSoundIntegerEntry(new SoundProfileEntry("debug-records", 300).setListSoundEventTypes(list)); + + listSoundProfiles.add(new SoundProfileEntry(".place", noisyInteractBuff)); + //listSoundProfiles.add(new SoundProfileEntry("generic.eat", 0.3D)); + listSoundProfiles.add(new SoundProfileEntry("player.burp", 1.1D)); + + //covers all note block sounds + listSoundProfiles.add(new SoundProfileEntry("block.note", noisyInteractBuff).setMaxDistToSpawnFromPlayer(64)); + + listSoundProfiles.add(new SoundProfileEntry("lever.click", noisyInteractBuff).setMaxDistToSpawnFromPlayer(noisyInteractRange)); + listSoundProfiles.add(new SoundProfileEntry("pressure_plate", noisyInteractBuff).setMaxDistToSpawnFromPlayer(noisyInteractRange)); + listSoundProfiles.add(new SoundProfileEntry("button.click", noisyInteractBuff).setMaxDistToSpawnFromPlayer(noisyInteractRange)); + + listSoundProfiles.add(new SoundProfileEntry("tripwire", noisyInteractBuff).setMaxDistToSpawnFromPlayer(noisyInteractRange)); + listSoundProfiles.add(new SoundProfileEntry("block.barrel", noisyInteractBuff).setMaxDistToSpawnFromPlayer(noisyInteractRange)); + + //long dist ones + if (ZAConfigFeatures.noisyZombies) listSoundProfiles.add(new SoundProfileEntry(SoundEvents.ZOMBIE_AMBIENT, 0.8D, ZAConfig.noisyZombiesReinforceOddsTo1).setMaxDistToSpawnFromPlayer(18)); + if (ZAConfigFeatures.noisyPistons) listSoundProfiles.add(new SoundProfileEntry(SoundEvents.PISTON_EXTEND, 2D, 20).setMaxDistToSpawnFromPlayer(24)); + + //not used traditionally, looked up directly + listSoundProfiles.add(new SoundProfileEntry(SoundEvents.GENERIC_EXPLODE, 3D).setMaxDistToSpawnFromPlayer(24)); + } + + public static void addSoundIntegerEntry(SoundProfileEntry entry) { + for (int soundID : entry.getListSoundEventTypes()) { + lookupSoundIntToProfile.put(soundID, entry); + } + //redundant now? + listSoundProfiles.add(entry); + } + + public static SoundProfileEntry getSoundIDEntry(int soundType) { + return lookupSoundIntToProfile.get(soundType); + } + + public static SoundProfileEntry getSoundIDEntry(String sound) { + if (lookupSoundNameToProfileCache.containsKey(sound)) { + return lookupSoundNameToProfileCache.get(sound); + } + for (SoundProfileEntry entry : listSoundProfiles) { + + if (entry.getSoundName().equals(sound)) { + lookupSoundNameToProfileCache.put(sound, entry); + return entry; + } else if (entry.isPartialMatchOnly() && sound.contains(entry.getSoundName())) { + lookupSoundNameToProfileCache.put(sound, entry); + return entry; + } + } + return null; + } + + public static void tickPlayer(Player player) { + + if (ZAConfigFeatures.wanderingHordes) { + + long lastWaypoint = lookupLastWaypointTime.containsKey(CoroUtilEntity.getName(player)) ? lookupLastWaypointTime.get(CoroUtilEntity.getName(player)) : 0L; + if (lastWaypoint < System.currentTimeMillis()) { + //System.out.println("lastWaypoint: " + lastWaypoint); + lastWaypoint = System.currentTimeMillis() + ZAConfigFeatures.frequencyOfWanderingHordesPerPlayer * 1000L; + lookupLastWaypointTime.put(CoroUtilEntity.getName(player), lastWaypoint); + //if (rand.nextInt(25) == 0) { + spawnWaypoint(player); + //} + } + } + + if (ZAConfigFeatures.awareness_Scent && !player.isCreative()) { + int lastHealth = lastHealths.containsKey(CoroUtilEntity.getName(player)) ? lastHealths.get(CoroUtilEntity.getName(player)) : 0; + Long lastBleedTime = lastBleedTimes.containsKey(CoroUtilEntity.getName(player)) ? lastBleedTimes.get(CoroUtilEntity.getName(player)) : 0L; + + Vector3d pos = new Vector3d(player.getX(), player.getY(), player.getZ()); + + if((int)player.getHealth() != lastHealth) { + if(player.getHealth() < lastHealth) { + EntityScent scent = spawnOrBuffSenseAtPos(player.level, pos, EnumSenseType.SCENT_BLOOD, ZAConfig.scentStrength); + ZombieAwareness.dbg("spawned or buffed scent sense from damage: " + scent.getStrengthPeak()); + } + + lastHealth = (int) player.getHealth(); + } + + lastHealths.put(CoroUtilEntity.getName(player), lastHealth); + + if(player.getHealth() / player.getMaxHealth() < 0.6F && lastBleedTime < System.currentTimeMillis()) { + lastBleedTime = System.currentTimeMillis() + 30000L; + lastBleedTimes.put(CoroUtilEntity.getName(player), lastBleedTime); + EntityScent scent = spawnOrBuffSenseAtPos(player.level, pos, EnumSenseType.SCENT_BLOOD, ZAConfig.scentStrength); + ZombieAwareness.dbg("spawned or buffed scent sense from bleeding: " + scent.getStrengthPeak()); + } + } + } + + public static void processMobSpawn(LivingSpawnEvent.SpecialSpawn event) { + if (ZAConfig.zombieRandSpeedBoost > 0) { + LivingEntity ent = event.getEntityLiving(); + + if (ent instanceof Zombie) { + if (!isMobSpeedBosted((Mob) ent)) { + giveRandomSpeedBoost((Mob) ent); + } + } + } + } + + public static boolean isMobSpeedBosted(Mob ent) { + return ent.getPersistentData().getBoolean(SPEED_BOOST_TAG); + } + + public static void giveRandomSpeedBoost(Mob ent) { + if (ZAConfig.zombieRandSpeedBoost > 0) { + double randBoost = ent.level.random.nextDouble() * ZAConfig.zombieRandSpeedBoost; + if (ent.isBaby()) { + randBoost *= -1; + } + AttributeModifier speedBoostModifier = new AttributeModifier(CoroUtilAttributes.SPEED_BOOST_UUID, "ZA speed boost", randBoost, AttributeModifier.Operation.MULTIPLY_BASE); + if (!ent.getAttribute(Attributes.MOVEMENT_SPEED).hasModifier(speedBoostModifier)) { + ZombieAwareness.dbg("boosting zombie speed to " + randBoost); + ent.getAttribute(Attributes.MOVEMENT_SPEED).addPermanentModifier(speedBoostModifier); + ent.getPersistentData().putBoolean(SPEED_BOOST_TAG, true); + } + } + } + + public static void huntTarget(Mob ent, LivingEntity targ, int pri) { + CoroUtilPath.tryMoveToEntityLivingLongDist(ent, targ, 1); + if (ent instanceof Mob) (ent).setTarget(targ); + } + + public static void huntTarget(Mob ent, LivingEntity targ) { + huntTarget(ent, targ, 0); + } + + public static boolean isEnemy(Entity ent, Entity targ) { + return isEnemy(ent, targ, false); + } + + public static boolean isEnemy(Entity ent, Entity targ, boolean omniTarget) { + if (targ instanceof LivingEntity) { + if (targ instanceof Player) { + if (!((Player) targ).isCreative() && ((Player) targ).getEffect(MobEffects.INVISIBILITY) == null) { + if (!omniTarget) { + return true; + } else if (ZAConfigPlayerLists.whiteListUsedOmniscient) { + if (ZAConfigPlayerLists.whitelistOmniscientTargettedPlayers.contains(CoroUtilEntity.getName(((Player) targ)))) { + if (ZAConfig.debugConsoleOmniscient) ZombieAwareness.dbg(CoroUtilEntity.getName((Player) targ) + " targetting omnisciently by " + ent); + return true; + } + } else { + return true; + } + } + } + return false; + } + return false; + } + + public static boolean sanityCheck(Entity ent, Entity entity1) { + return true; + } + + public static void markPerformedPathing(Mob ent) { + ent.setNoActionTime(0); + ent.getPersistentData().putLong(ZA_LAST_ACTION, ent.level.getGameTime()); + } + + public static void tickAI(Mob ent) { + //if (true) return; + //ZAConfig.debugConsoleSuperDetailed = false; + if (ZAConfig.debugConsoleSuperDetailed) ZombieAwareness.dbg("ZA DBG: Ticking: " + ent); + + long lastActionTime = ent.getPersistentData().getLong(ZA_LAST_ACTION); + if (lastActionTime > 0 && ent.level.getGameTime() - ZAConfig.tickCooldownBetweenPathfinds < lastActionTime) return; + + //if (ent.getNoActionTime() <= 40) return; + + //A more performance friendly omniscient, only runs it when no target, but still allows for smaller ranged retargetting + //adding entity ID onto world time to stagger processing per entity more, should improve TPS + if ((ent.level.getGameTime() + ent.getId()) % 40 == 0) { + if (ZAConfig.omniscient && ent.getTarget() == null) { + ai_FindTarget(ent, true); + } else { + ai_FindTarget(ent, false); + } + } + + EntityScent senseTracked = null; + + if (ent.getTarget() == null && (ent.getNavigation().isDone())) { + //Find player made senses + if (!ZAConfig.awareness_Light_OnlyZombies || (ent instanceof Zombie)) { + if (!ZAConfigFeatures.awareness_Light || !ai_FindLightSource(ent)) { + if (ent.level.random.nextInt(3) == 0) { + senseTracked = ai_FindSense(ent, true); + } + + } + } else { + senseTracked = ai_FindSense(ent); + } + } + + if (senseTracked != null && ent.getNavigation().getPath() != null) { + Node pathTo = ent.getNavigation().getPath().getEndNode(); + if (pathTo != null) { + Player player = getClosestPlayer(ent.level, pathTo.x, pathTo.y, pathTo.z, 6D); + if (player != null) { + //tryPlayInvestigateSound(ent, new Vec3d(ent.getX(), ent.getY(), ent.getZ())); + tryPlayInvestigateSound(ent, new Vector3d(pathTo.x, pathTo.y, pathTo.z)); + } + + } + + } + + tickCustomMob(ent); + } + + public static void tickCustomMob(Mob ent) { + if (ZAConfigFeatures.wanderingHordes) { + if (ent instanceof Spider) { + if (ent.getPassengers().size() > 0 && ent.getPassengers().get(0) instanceof Skeleton) { + if (ent.level.random.nextInt(100) == 0) { + spawnWaypoint(ent); + } + } + } + } + } + + public static boolean ai_FindLightSource(Mob ent) { + + if (ent.level.isDay()) return false; + + boolean forceOn = false; + + if (forceOn || ent.level.random.nextInt(3) == 0) { + + float lightValueAtEntity = ent.level.getBrightness(ent.blockPosition()); + + Random rand = new Random(); + + int size; + + for (int i = 0; i < 4; i++) { + Player entP = getClosestPlayerToEntity(ent.level, ent, 999); + if (entP != null) { + + //adjusted from 32 for 1.16 to get better results, vanilla might have changed something + size = 32 * (i+1); + //size = 12 * (i+1); + //size = 4; + + int rX = (int) Math.floor(entP.getX() + (rand.nextInt(size) - (size/2))); + int rY = (int) Math.floor(entP.getY() + (rand.nextInt(size/2) - (size/4))); + int rZ = (int) Math.floor(entP.getZ() + (rand.nextInt(size) - (size/2))); + + BlockPos pos = new BlockPos(rX, rY, rZ); + + if (!ent.level.isLoaded(pos)) continue; + + float lightValue = entP.level.getBrightness(pos); + + //as of 1.16 its float values for brightness, 0.2 seems like a good minimum brightness + //moon phases doesnt seem to affect this value + + //if bright enough and also as bright or brighter than where they are currently + if (forceOn || lightValue > 0.2F/* && lightValue >= lightValueAtEntity*/) { + //adjusted to 32 from 64 + if ((forceOn || ent.level.random.nextInt(5) == 0) && ent.distanceTo(entP) > 16) { + boolean canSeePos = CoroUtilEntity.canSee(ent, new BlockPos(rX, rY, rZ)); + if (canSeePos) { + ZombieAwareness.dbg("try path to light source - " + rX + ", " + rY + ", " + rZ); + if (ent.getNavigation().moveTo(rX, rY, rZ, 1)) { + //if (CoroUtilPath.tryMoveToXYZLongDist(ent, rX, rY, rZ, 1)) { + ZombieAwareness.dbg("node count: " + ent.getNavigation().getPath().getNodeCount()); + + ZombieAwareness.dbg("pathing to lightsource at " + rX + ", " + rY + ", " + rZ + " - " + ent); + markPerformedPathing(ent); + } + return true; + } + } + } + } + } + } + + return false; + } + + public static EntityScent ai_FindSense(Mob ent) { + return ai_FindSense(ent, true); + } + + public static EntityScent ai_FindSense(Mob ent, boolean includeWaypoints) { + + EntityScent var3 = getSenseNearEntity(ent); + + if(var3 != null) { + if (includeWaypoints || var3.type != 2) { + if (CoroUtilPath.tryMoveToEntityLivingLongDist(ent, var3, 1)) { + markPerformedPathing(ent); + ZombieAwareness.dbg("ai_FindSense call, type: " + ((EntityScent)var3).type + " - " + ent.getName() + " -> " + var3.position()); + return var3; + } + } + } + + return null; + } + + public static boolean ai_FindTarget(Mob ent, boolean omniscient) { + long huntRange = ZAConfig.sightRange; + + if (omniscient) huntRange = 512; + + if ((ent.getTarget() == null || ent.level.random.nextInt(100) == 0)) { + boolean found = false; + Entity clEnt = null; + float closest = 9999F; + List list = ent.level.getEntities(ent, ent.getBoundingBox().inflate(huntRange, huntRange/2, huntRange)); + for(int j = 0; j < list.size(); j++) + { + Entity entity1 = (Entity)list.get(j); + + //for new calmed zombies, keep them from targetting/pathing towards player + //just blacklisting all of them from this feature, not checking if calm currently + if (ent.getClass().getSimpleName().equals("EntityZombiePlayer")) { + continue; + } + + if(isEnemy(ent, entity1, omniscient)) + { + if (omniscient || (ZAConfig.seeThroughWalls || ((LivingEntity) entity1).hasLineOfSight(ent))) { + if (sanityCheck(ent, entity1)) { + float dist = (float) ent.distanceToSqr(entity1); + if (dist < closest) { + closest = dist; + clEnt = entity1; + } + } + } + } + } + if (clEnt != null) { + huntTarget(ent, (LivingEntity)clEnt); + markPerformedPathing(ent); + ZombieAwareness.dbg(" hunting target " + ent + " " + clEnt); + return true; + } + } + return false; + } + + /** + * Gets sense within range provided sense is strong enough, has random chance and doesnt always return closest sense + * + * @param entSource + * @return + */ + public static EntityScent getSenseNearEntity(Entity entSource) { + List listEnts = entSource.level.getEntities(entSource, entSource.getBoundingBox().inflate(ZAConfig.maxPFRangeSense, ZAConfig.maxPFRangeSense, ZAConfig.maxPFRangeSense)); + + EntityScent entBest = null; + + for(int i = 0; i < listEnts.size(); ++i) { + Entity entCheck = listEnts.get(i); + + if (entCheck instanceof EntityScent) { + + double dist = entSource.distanceTo(entCheck); + + float rangeToClose = 5F; + int randChance = 20; + if (((EntityScent)entCheck).type == 2) { + rangeToClose = 10F; + randChance = 2; + } + + if (dist < ((EntityScent)entCheck).getRange() && dist > rangeToClose && entSource.level.random.nextInt(randChance) == 0) { + entBest = (EntityScent) entCheck; + return entBest; + } + } + } + + return entBest; + } + + public static void hookPlayEvent(int type, Level world, double pX, double pY, double pZ, int data) { + + addSoundHooks(); + + //if event type is for playing a record + if (world.isClientSide() || !canSpawnTraceQuickCheck(world)) return; + + SoundProfileEntry entry = getSoundIDEntry(type); + if (entry != null) { + Player closestPlayer = getClosestPlayer(world, pX, pY, pZ, entry.getMaxDistToSpawnFromPlayer()); + if (closestPlayer != null) { + + Vector3d pos = new Vector3d(pX, pY, pZ); + if (!canSpawnTrace(world, pX, pY, pZ)) { + return; + } + + handleSoundProfileEvent(world, entry, pos, closestPlayer); + } + } + } + + public static void hookSoundEvent(SoundEvent sound, Level world, double x, double y, double z, float volume, float pitch) { + + addSoundHooks(); + + if (world.isClientSide() || sound == null || !canSpawnTraceQuickCheck(world)) return; + + String soundName = SoundProfileEntry.getSoundEventName(sound); + SoundProfileEntry entry = getSoundIDEntry(soundName); + + if (entry != null) { + Player closestPlayer = getClosestPlayer(world, x, y, z, entry.getMaxDistToSpawnFromPlayer()); + if (closestPlayer != null) { + Vector3d pos = new Vector3d(x, y, z); + if (!canSpawnTrace(world, x, y, z)) { + //spawn 1 higher up, added for tripwire detection, cant spawn sense on tripwire + y += 1; + if (!canSpawnTrace(world, x, y, z)) { + return; + } + } + handleSoundProfileEvent(world, entry, pos, closestPlayer); + } + } + } + + public static void handleSoundProfileEvent(Level world, SoundProfileEntry entry, Vector3d pos, Player closestPlayer) { + double distToPlayer = Math.sqrt(closestPlayer.distanceToSqr(pos.x, pos.y, pos.z)); + double strength = ZAConfig.soundStrength; + if (distToPlayer <= entry.getMaxDistToSpawnFromPlayer()) { + if (entry.getOddsTo1ToUse() <= 0 || rand.nextInt(entry.getOddsTo1ToUse()) == 0) { + strength *= entry.getMultiplier(); + + EntityScent scent = spawnOrBuffSenseAtPos(world, pos, EnumSenseType.SOUND, (int)strength); + + ZombieAwareness.dbg("spawned or buffed sound sense from soundEvent, sound: " + entry.getSoundName() + ", str: " + scent.getStrengthPeak()/* + ", vol: " + volume*/); + } + } + } + + public static void hookBlockEvent(PlayerEvent event, int chance) { + if (event.getEntity() != null && !canSpawnTraceQuickCheck(event.getEntity().level)) return; + + if (event.getPlayer() == null || (ZAConfigPlayerLists.whiteListUsedSenses && !ZAConfigPlayerLists.whitelistSenses.contains(CoroUtilEntity.getName(event.getPlayer())))) return; + + if (!event.getEntity().level.isClientSide() && event.getEntity().level.random.nextInt(chance) == 0) { + + int strength = ZAConfig.soundStrength; + Vector3d pos = new Vector3d(event.getPlayer().getX(), event.getPlayer().getY(), event.getPlayer().getZ()); + + EntityScent scent = spawnOrBuffSenseAtPos(event.getEntity().level, pos, EnumSenseType.SOUND, strength); + + ZombieAwareness.dbg("spawned or buffed sound sense from PlayerEvent: " + scent.getStrengthPeak()); + } + } + + /** + * Player can be null + * + * @param player + * @param chance + */ + public static void handleBlockBasedEvent(Player player, Level world, BlockPos pos, int chance) { + if (player == null && ZAConfig.blockBreakEvent_PlayersOnly) { + return; + } + + if (!canSpawnTraceQuickCheck(world)) return; + + if (player != null && ZAConfigPlayerLists.whiteListUsedSenses) { + if (ZAConfigPlayerLists.whitelistSenses.contains(CoroUtilEntity.getName(player))) return; + } + + if (!world.isClientSide() && world.random.nextInt(chance) == 0) { + + int strength = ZAConfig.soundStrength; + Vector3d posVec = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); + + EntityScent scent = spawnOrBuffSenseAtPos(world, posVec, EnumSenseType.SOUND, strength); + + ZombieAwareness.dbg("spawned or buffed sound sense from BlockBasedEvent: " + scent.getStrengthPeak()); + } + } + + public static void hookSetAttackTarget(LivingSetAttackTargetEvent event) { + + //ZombieAwareness.dbg(event.getEntityLiving().getEntityId() + " targetting " + event.getTarget()); + + if (event.getEntityLiving() instanceof Mob) { + if (event.getTarget() instanceof Player) { + //tryPlayAlertSound((EntityLiving)event.getEntityLiving(), new Vec3d(event.getTarget().getX(), event.getTarget().getY(), event.getTarget().getZ())); + tryPlayTargetSound((Mob)event.getEntityLiving(), (LivingEntity)event.getTarget(), new Vector3d(event.getEntityLiving().getX(), event.getEntityLiving().getY(), event.getEntityLiving().getZ())); + } else if (event.getTarget() == null) { + //dont use, AI stupidly detargets when resetting tasks despite still chasing player, causing double alert noise if this code is used + /*if (lookupLastAlertTime.containsKey(event.getEntityLiving())) { + lookupLastAlertTime.remove(event.getEntityLiving()); + System.out.println("detarget"); + }*/ + } + } + + } + + public static void spawnWaypoint(Entity entSource) { + int range = 128; + + double tryX = (int)entSource.getX() - (range/2) + (rand.nextInt(range)); + double tryZ = (int)entSource.getZ() - (range/2) + (rand.nextInt(range)); + double tryY = entSource.level.getHeight(Heightmap.Types.MOTION_BLOCKING, (int)Math.floor(tryX), (int)Math.floor(tryZ)); + + if (!canSpawnTrace(entSource.level, tryX, tryY, tryZ)) { + return; + } + + double height = entSource.getY(); + + EntityScent var1 = getSenseNodeAtPos(entSource.level, new Vector3d(tryX, tryY, tryZ), EnumSenseType.WAYPOINT); + + boolean newNode = false; + + if (var1 == null) { + var1 = new EntityScent(EntityRegistry.SCENT, entSource.level); + newNode = true; + } + + var1.setStrengthPeak(150); + + if (newNode) { + var1.setPos(tryX, tryY, tryZ); + var1.type = 2; + + entSource.level.addFreshEntity(var1); + } + + //System.out.println("spawning new waypoint for "); + if (debug) System.out.println("WP: " + entSource + " - range: " + var1.getRange()); + } + + public static boolean canSpawnTraceQuickCheck(Level world) { + if (!ZAConfigFeatures.awareness_Sound) { + return false; + } + if (ZAConfigFeatures.awareness_Sound_OverworldOnly) { + if (world.dimension() != Level.OVERWORLD) return false; + } + return true; + } + + public static boolean canSpawnTrace(Level world, double x, double y, double z) { + BlockPos pos = new BlockPos(x,y,z); + if (!world.isLoaded(pos)) return false; + BlockState state = world.getBlockState(pos); + //iirc circuits check was to prevent senses spawning on pressure plates and triggering them, but there should be better ways to stop that... + //might be redundant since AABB and canTriggerWalking and canBeCollidedWith fix + if (state.getMaterial() == Material.DECORATION && (!(state.getBlock() instanceof ButtonBlock) && !(state.getBlock() instanceof LeverBlock))) { + return false; + } + return true; + } + + public static Player getClosestPlayerToEntity(Level world, Entity par1Entity, double par2) + { + return getClosestPlayer(world, par1Entity.getX(), par1Entity.getY(), par1Entity.getZ(), par2); + } + + /** + * Gets the closest player to the point within the specified distance (distance can be set to less than 0 to not + * limit the distance). Args: x, y, z, dist + */ + public static Player getClosestPlayer(Level world, double x, double y, double z, double maxDistance) + { + double closestDist = -1.0D; + Player closestPlayer = null; + + for (int i = 0; i < world.players().size(); ++i) + { + Player entityplayer1 = world.players().get(i); + if (!ZAConfigPlayerLists.whiteListUsedSenses || ZAConfigPlayerLists.whitelistSenses.contains(CoroUtilEntity.getName(entityplayer1))) { + double d5 = entityplayer1.distanceToSqr(x, y, z); + + if ((maxDistance < 0.0D || d5 < maxDistance * maxDistance) && (closestDist == -1.0D || d5 < closestDist)) + { + closestDist = d5; + closestPlayer = entityplayer1; + } + } + } + + return closestPlayer; + } + + /** + * Checks if a scent of the same type is already at this location + * + * @param parWorld + * @param parPos + * @param type + * @return + */ + public static EntityScent getSenseNodeAtPos(Level parWorld, Vector3d parPos, EnumSenseType type) { + + if (ZAConfig.extraScentCutoffRange == -1) return null; + + AABB aabb = new AABB(parPos.x, parPos.y, parPos.z, parPos.x + 1, parPos.y + 1, parPos.z + 1); + aabb = aabb.inflate(ZAConfig.extraScentCutoffRange, ZAConfig.extraScentCutoffRange, ZAConfig.extraScentCutoffRange); + + List list = parWorld.getEntitiesOfClass(EntityScent.class, aabb); + + if (list.size() > 0) { + for(int j = 0; j < list.size(); j++) + { + EntityScent node = (EntityScent)list.get(j); + if (node.type == type.ordinal()) { + return node; + } + } + } + + return null; + } + + public static EntityScent spawnOrBuffSenseAtPos(Level world, Vector3d parPos, EnumSenseType type, int strength) { + return spawnOrBuffSenseAtPos(world, parPos, type, strength, true); + } + + /** + * Tries to spawn a new sense, if one is close enough, it will multiply that senses current strength by lastMultiply + * + * @param world + * @param parPos + * @param type + * @param strength + * @return + */ + public static EntityScent spawnOrBuffSenseAtPos(Level world, Vector3d parPos, EnumSenseType type, int strength, boolean frequentSoundMultiply) { + + EntityScent sense = getSenseNodeAtPos(world, parPos, type); + + if (sense == null) { + sense = new EntityScent(EntityRegistry.SCENT, world); + sense.type = type.ordinal(); + sense.setPos(parPos.x, parPos.y, parPos.z); + sense.setStrengthPeak(strength); + world.addFreshEntity(sense); + } else if (frequentSoundMultiply) { + //instead of amplifying current strength, amp the base value, but only if current strength is weaker than param + float str = sense.getStrengthPeak(); + if (str < strength) { + str = strength; + } + + if(sense.lastBuffTime + (long)ZAConfig.frequentSoundThreshold > System.currentTimeMillis()) { + sense.lastMultiply += 0.1F; + str *= sense.lastMultiply; + } else { + sense.lastMultiply = 1.0F; + } + + sense.lastBuffTime = System.currentTimeMillis(); + sense.setStrengthPeak((int)str); + } + + return sense; + } + + public static void tryPlayTargetSound(Mob entAlerted, LivingEntity entTargetted, Vector3d pos) { + + if (!ZAConfigFeatures.soundAlerts) return; + + if (!ZombieAwareness.canProcessEntity(entAlerted)) return; + + //prevent target spam sound from omniscient zombies from HW-inv + /*if (entAlerted.getEntityData().hasKey(UtilEntityBuffs.dataEntityBuffed_AI_Omniscience)) { + return; + }*/ + + //added max dist and blocks loaded check due to https://github.com/Corosauce/ZombieAwareness/issues/11 + double distMaxCancel = 75; + if (!lookupLastAlertTime.containsKey(entAlerted) || lookupLastAlertTime.get(entAlerted) + alertDelay < entAlerted.level.getGameTime()) { + if (entAlerted.distanceTo(entTargetted) < distMaxCancel + && entAlerted.level.dimension() == entTargetted.level.dimension() + && entAlerted.level.isLoaded(entAlerted.blockPosition()) + && entTargetted.level.isLoaded(entTargetted.blockPosition())) { + if (entAlerted.hasLineOfSight(entTargetted)) { + //entAlerted.level.playSound(null, pos.x, pos.y, pos.z, SoundRegistry.get("target"), SoundCategory.HOSTILE, 3F, 0.8F + (entAlerted.level.random.nextFloat() * 0.2F)); + entAlerted.level.playSound(null, entTargetted.getX(), entTargetted.getY(), entTargetted.getZ(), ZAConfigFeatures.soundUseAlternateAlertNoise ? SoundRegistry.get("alert") : SoundRegistry.get("target"), SoundSource.HOSTILE, (float) ZAConfigFeatures.soundVolumeAlertTarget, ZAConfigFeatures.soundUseAlternateAlertNoise ? 1F : 0.8F + (entAlerted.level.random.nextFloat() * 0.2F)); + lookupLastAlertTime.put(entAlerted, entAlerted.level.getGameTime()); + //ZombieAwareness.dbg("!!! alert play for ent: " + entAlerted.getEntityId() + ", lookupSize: " + lookupLastAlertTime.size()); + } else { + //likely due to new call for help routine in vanilla, so treat it like investigating until line of sight is made + tryPlayInvestigateSound(entAlerted, pos); + //ZombieAwareness.dbg("??? tried play alert for no LOS entity: " + entAlerted.getEntityId() + ", lookupSize: " + lookupLastAlertTime.size()); + } + } + } else { + //ZombieAwareness.dbg("already played alert for ent: " + entAlerted.getEntityId() + ", lookupSize: " + lookupLastAlertTime.size()); + } + } + + public static void tryPlayInvestigateSound(Mob entAlerted, Vector3d pos) { + + if (!ZAConfigFeatures.soundInvestigates) return; + + if (!ZombieAwareness.canProcessEntity(entAlerted)) return; + + if (!lookupLastInvestigateTime.containsKey(entAlerted) || lookupLastInvestigateTime.get(entAlerted) + investigateDelay < entAlerted.level.getGameTime()) { + entAlerted.level.playSound(null, pos.x, pos.y, pos.z, SoundRegistry.get("investigate"), SoundSource.HOSTILE, (float)ZAConfigFeatures.soundVolumeInvestigate, 0.7F + (entAlerted.level.random.nextFloat() * 0.3F)); + lookupLastInvestigateTime.put(entAlerted, entAlerted.level.getGameTime()); + ZombieAwareness.dbg("!!! investigate play for ent: " + entAlerted.getId() + ", lookupSize: " + lookupLastInvestigateTime.size()); + } + } + + public static boolean isZombieAwarenessActive(Level world) { + if (world == null) return false; + if (ZAConfig.daysBeforeFeaturesActivate <= 0) return true; + if (((double)world.getGameTime() / CoroUtilWorldTime.getDayLength()) >= ZAConfig.daysBeforeFeaturesActivate) { + return true; + } else { + return false; + } + } +} diff --git a/src/main/java/com/corosus/zombieawareness/ZombieAwareness.java b/src/main/java/com/corosus/zombieawareness/ZombieAwareness.java new file mode 100644 index 0000000..76bc474 --- /dev/null +++ b/src/main/java/com/corosus/zombieawareness/ZombieAwareness.java @@ -0,0 +1,201 @@ +package com.corosus.zombieawareness; + +import com.corosus.zombieawareness.client.ClientRegistry; +import com.corosus.zombieawareness.config.*; +import com.corosus.modconfig.ConfigMod; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.server.ServerStartingEvent; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.config.ModConfig; +import net.minecraftforge.fml.event.config.ModConfigEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.fml.loading.FMLPaths; +import net.minecraftforge.fml.loading.FileUtils; +import net.minecraftforge.registries.ForgeRegistries; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Mod(ZombieAwareness.MODID) +public class ZombieAwareness +{ + public static final Logger LOGGER = LogManager.getLogger(); + + public static final String MODID = "zombieawareness"; + + public ZombieAwareness() { + IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus(); + DistExecutor.safeRunForDist(() -> ClientRegistry::new, () -> EventRegistry::new); + + MinecraftForge.EVENT_BUS.register(this); + MinecraftForge.EVENT_BUS.register(new EntityRegistry()); + MinecraftForge.EVENT_BUS.register(new ZAEventHandler()); + MinecraftForge.EVENT_BUS.addListener(this::serverStart); + + FileUtils.getOrCreateDirectory(FMLPaths.CONFIGDIR.get().resolve(MODID), MODID); + ConfigMod.addConfigFile(MODID, new ZAConfig()); + ConfigMod.addConfigFile(MODID, new ZAConfigClient()); + ConfigMod.addConfigFile(MODID, new ZAConfigFeatures()); + //ConfigMod.addConfigFile(MODID, new ZAConfigMobLists()); + ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, MobListsConfig.CONFIG, ZombieAwareness.MODID + File.separator + "MobLists.toml"); + ConfigMod.addConfigFile(MODID, new ZAConfigPlayerLists()); + //ConfigMod.addConfigFile(MODID, new ZAConfigSpawning()); + ZombieAwareness.generateEntityTickList(); + //required to make forge tell us when our mods reload, and we then tell ModConfig about it so it does its thing + modBus.addListener(this::onReload); + } + + @SubscribeEvent + public void onReload(final ModConfigEvent.Reloading configEvent) { + clearConfigCache(); + ConfigMod.onReload(configEvent); + } + + @SubscribeEvent + public void serverStart(ServerStartingEvent event) { + clearConfigCache(); + } + + public static void clearConfigCache() { + ZAUtil.lookupTickableEntitiesCache.clear(); + } + + public static void dbg(Object obj) { + if (ZAConfig.debugConsole) { + System.out.println(obj); + } + } + + public static boolean canProcessEntity(Entity ent) { + if (!canEntityBeProcessedOverride(ent)) { + return false; + } + return canProcessEntity(ent.getType(), false); + } + + /** + * Looks up and generates config info if entry missing + * + * @param ent + * @param pregen + * @return + */ + public static boolean canProcessEntity(EntityType ent, boolean pregen) { + + String entName = getEntityRegisteredName(ent); + if (ZAUtil.lookupTickableEntitiesCache.containsKey(ent)) + { + return ZAUtil.lookupTickableEntitiesCache.get(ent); + } + + boolean result = false; + if (canConfigEntity(ent)) { + //if (!pregen) config.load(); + boolean canProcess = getDefaultForEntity(ent); + try { + //prevent crash for case where mod entity can be null or blank + if (entName != null && !entName.equals("")) { + //result = config.get(configCategory, entName, canProcess).getBoolean(canProcess); + result = canProcess; + } + } catch (Exception ex) { + ex.printStackTrace(); + } + //if (!pregen) config.save(); + ZAUtil.lookupTickableEntitiesCache.put(ent, result); + } + + return result; + } + + /** + * Handles special cases for specific instances like if owned and has an owner. But I want that to never be processed anyways + * Placeholder for now until more dynamic needs are found + * + * @param entity + * @return false if you want to cancel processing, true lets it continue with other rules + */ + public static boolean canEntityBeProcessedOverride(Entity entity) { + return true; + /*boolean result = false; + if (entity instanceof IEntityOwnable) { + if (entity. + }*/ + } + + /** + * Used to avoid adding entries to config that cant be used even if set to true + * + * @param ent + * @return + */ + public static boolean canConfigEntity(EntityType ent) { + //return MonsterEntity.class.isAssignableFrom(ent.getClass()) || IMob.class.isAssignableFrom(ent.getClass()); + return ent.getCategory() == MobCategory.MONSTER; + } + + public static boolean getDefaultForEntity(EntityType ent) { + if (canConfigEntity(ent)) { + if (MobListsConfig.GENERAL.enhancedMobs.get().contains(ent.getRegistryName().toString())) { + return true; + } else { + return false; + } + } + return false; + } + + public static List getListFromCSV(String csv) { + return Stream.of(csv.split(",")) + .map(String::trim) + .collect(Collectors.toList()); + } + + /** + * 1.10.2: only name available + * 1.11.2: 'name' field in EntityEntry, new registered name is the snake case key for ForgeRegistries.ENTITIES.getEntries() as a resource location + * 1.16.5: rip class inheritance info + * + * @param ent + * @return + */ + public static String getEntityRegisteredName(EntityType ent) { + try { + + return ent.getRegistryName().toString(); + } catch (Exception ex) { + if (ZAConfig.debugConsole) { + ex.printStackTrace(); + } + return ent.getClass().getSimpleName(); + + } + } + + /** + * Generates list of entities we can process, these are written to config they can modify every entities config after first run + * + */ + public static void generateEntityTickList() { + for(Map.Entry>, EntityType> entry : ForgeRegistries.ENTITIES.getEntries()) { + //calling canProcessEntity fills the lists + boolean tickEnt = canProcessEntity(entry.getValue(), true); + /*if (tickEnt) { + MobListsConfig.enhancedMobsDefaults.add(entry.getValue().getRegistryName().toString()); + }*/ + } + } +} diff --git a/src/main/java/com/corosus/zombieawareness/client/ClientRegistry.java b/src/main/java/com/corosus/zombieawareness/client/ClientRegistry.java new file mode 100644 index 0000000..9bc58d5 --- /dev/null +++ b/src/main/java/com/corosus/zombieawareness/client/ClientRegistry.java @@ -0,0 +1,20 @@ +package com.corosus.zombieawareness.client; + +import com.corosus.zombieawareness.EntityRegistry; +import net.minecraft.client.renderer.entity.EntityRenderers; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; + +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) +public class ClientRegistry { + + @OnlyIn(Dist.CLIENT) + @SubscribeEvent + public static void registerModels(FMLClientSetupEvent event) { + EntityRenderers.register(EntityRegistry.SCENT, render -> new RenderScent(render)); + } + +} diff --git a/src/main/java/com/corosus/zombieawareness/client/RenderScent.java b/src/main/java/com/corosus/zombieawareness/client/RenderScent.java new file mode 100644 index 0000000..9e5d40c --- /dev/null +++ b/src/main/java/com/corosus/zombieawareness/client/RenderScent.java @@ -0,0 +1,154 @@ +package com.corosus.zombieawareness.client; + +import com.corosus.zombieawareness.ZombieAwareness; +import com.corosus.zombieawareness.EntityScent; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.*; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Mob; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.phys.AABB; +import net.minecraft.core.BlockPos; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.minecraft.world.level.LevelReader; + +import com.corosus.zombieawareness.config.ZAConfigClient; + +import com.mojang.blaze3d.vertex.Tesselator; +import net.minecraft.client.Camera; + +import java.util.Random; + +public class RenderScent extends EntityRenderer { + + public static ResourceLocation TEXTURE64 = new ResourceLocation(ZombieAwareness.MODID + ":textures/entities/bloodx64.png"); + private static final RenderType SHADOW_RENDER_TYPE = RenderType.entityShadow(TEXTURE64); + + protected RenderScent(EntityRendererProvider.Context p_i46179_1_) { + super(p_i46179_1_); + } + + @Override + public ResourceLocation getTextureLocation(Entity p_110775_1_) { + return TEXTURE64; + } + + @Override + public void render(Entity pEntity, float pEntityYaw, float pPartialTicks, PoseStack pMatrixStack, MultiBufferSource pBuffer, int pPackedLight) { + super.render(pEntity, pEntityYaw, pPartialTicks, pMatrixStack, pBuffer, pPackedLight); + //bindEntityTexture(var1); + float shadowSize = 1.0F; + float shadowStrength = 1.0F; + this.shadowStrength = 1; + this.shadowRadius = 1; + //GL11.glPushMatrix(); + if (ZAConfigClient.client_renderBlood && (((EntityScent)pEntity).type == 0)) { + //this.doRenderNode(var1, var2, var4, var6, var8, var9); + float scale = 0.7F + ((float)((EntityScent)pEntity).getAgeScale() * 0.3F); + float alpha = (float)((EntityScent)pEntity).getAgeScale(); + renderBlood(pMatrixStack, pBuffer, pEntity, shadowStrength, pPartialTicks, pEntity.level, scale, alpha); + } + this.shadowStrength = 0; + this.shadowRadius = 0; + //shadowSize = 0.0F; + //GL11.glPopMatrix(); + } + + private static void renderBlood(PoseStack p_229096_0_, MultiBufferSource p_229096_1_, Entity p_229096_2_, float p_229096_3_, float p_229096_4_, LevelReader p_229096_5_, float p_229096_6_, float alpha) { + float f = p_229096_6_; + if (p_229096_2_ instanceof Mob) { + Mob mobentity = (Mob)p_229096_2_; + if (mobentity.isBaby()) { + f = p_229096_6_ * 0.5F; + } + } + + double d2 = Mth.lerp((double)p_229096_4_, p_229096_2_.xOld, p_229096_2_.getX()); + double d0 = Mth.lerp((double)p_229096_4_, p_229096_2_.yOld, p_229096_2_.getY()); + double d1 = Mth.lerp((double)p_229096_4_, p_229096_2_.zOld, p_229096_2_.getZ()); + int i = Mth.floor(d2 - (double)f); + int j = Mth.floor(d2 + (double)f); + int k = Mth.floor(d0 - (double)f); + int l = Mth.floor(d0); + int i1 = Mth.floor(d1 - (double)f); + int j1 = Mth.floor(d1 + (double)f); + PoseStack.Pose matrixstack$entry = p_229096_0_.last(); + VertexConsumer ivertexbuilder = p_229096_1_.getBuffer(SHADOW_RENDER_TYPE); + + //TODO: either integrate cleanly with entity renderer (no nested rendering), or make it particles + + Tesselator tessellator = Tesselator.getInstance(); + //BufferBuilder bufferbuilder = tessellator.getBuilder(); + //((BufferBuilder) ivertexbuilder).begin(GL11.GL_QUADS, DefaultVertexFormats.NEW_ENTITY); + + for(BlockPos blockpos : BlockPos.betweenClosed(new BlockPos(i, k, i1), new BlockPos(j, l, j1))) { + renderBlockShadow(matrixstack$entry, ivertexbuilder, p_229096_5_, blockpos, d2, d0, d1, f, p_229096_3_, alpha); + } + + Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + //SHADOW_RENDER_TYPE.end((BufferBuilder) ivertexbuilder, 0, 0, 0); + //SHADOW_RENDER_TYPE.end((BufferBuilder) ivertexbuilder, camera.getPosition().x, camera.getPosition().y, camera.getPosition().z); + + } + + private static void renderBlockShadow(PoseStack.Pose p_229092_0_, VertexConsumer p_229092_1_, LevelReader p_229092_2_, BlockPos p_229092_3_, double p_229092_4_, double p_229092_6_, double p_229092_8_, float p_229092_10_, float p_229092_11_, float alpha) { + BlockPos blockpos = p_229092_3_.below(); + BlockState blockstate = p_229092_2_.getBlockState(blockpos); + + /*float light = (float)p_229092_2_.getMaxLocalRawBrightness(p_229092_3_) / 15F; + light = (float)p_229092_2_.getBrightness(p_229092_3_);*/ + //TODO: until i fix proper, just have it less blaringly bright + float light = 0.5F; + //light = (float)LevelRenderer.getLightColor(p_229092_2_, blockpos) / (float)15728880; + //System.out.println(light); + //light = 0.2F; + //if (blockstate.getRenderShape() != BlockRenderType.INVISIBLE && p_229092_2_.getMaxLocalRawBrightness(p_229092_3_) > 3) { + if (blockstate.isCollisionShapeFullBlock(p_229092_2_, blockpos)) { + VoxelShape voxelshape = blockstate.getShape(p_229092_2_, p_229092_3_.below()); + if (!voxelshape.isEmpty()) { + float f = (float)(((double)p_229092_11_ - (p_229092_6_ - (double)p_229092_3_.getY()) / 2.0D) * 0.5D * (double)p_229092_2_.getBrightness(p_229092_3_)); + f = alpha; + if (f >= 0.0F) { + if (f > 1.0F) { + f = 1.0F; + } + + AABB axisalignedbb = voxelshape.bounds(); + double d0 = (double)p_229092_3_.getX() + axisalignedbb.minX; + double d1 = (double)p_229092_3_.getX() + axisalignedbb.maxX; + double d2 = (double)p_229092_3_.getY() + axisalignedbb.minY; + double d3 = (double)p_229092_3_.getZ() + axisalignedbb.minZ; + double d4 = (double)p_229092_3_.getZ() + axisalignedbb.maxZ; + float f1 = (float)(d0 - p_229092_4_); + float f2 = (float)(d1 - p_229092_4_); + float f3 = (float)(d2 - p_229092_6_); + float f4 = (float)(d3 - p_229092_8_); + float f5 = (float)(d4 - p_229092_8_); + float f6 = -f1 / 2.0F / p_229092_10_ + 0.5F; + float f7 = -f2 / 2.0F / p_229092_10_ + 0.5F; + float f8 = -f4 / 2.0F / p_229092_10_ + 0.5F; + float f9 = -f5 / 2.0F / p_229092_10_ + 0.5F; + shadowVertex(p_229092_0_, p_229092_1_, f, f1, f3, f4, f6, f8, light); + shadowVertex(p_229092_0_, p_229092_1_, f, f1, f3, f5, f6, f9, light); + shadowVertex(p_229092_0_, p_229092_1_, f, f2, f3, f5, f7, f9, light); + shadowVertex(p_229092_0_, p_229092_1_, f, f2, f3, f4, f7, f8, light); + } + + } + } + //} + } + + private static void shadowVertex(PoseStack.Pose p_229091_0_, VertexConsumer p_229091_1_, float p_229091_2_, float p_229091_3_, float p_229091_4_, float p_229091_5_, float p_229091_6_, float p_229091_7_, float light) { + p_229091_1_.vertex(p_229091_0_.pose(), p_229091_3_, p_229091_4_, p_229091_5_).color(1.0F * light, 1.0F * light, 1.0F * light, p_229091_2_).uv(p_229091_6_, p_229091_7_).overlayCoords(OverlayTexture.NO_OVERLAY).uv2(15728880).normal(p_229091_0_.normal(), 0.0F, 1.0F, 0.0F).endVertex(); + } + +} diff --git a/src/main/java/ZombieAwareness/SoundProfileEntry.java b/src/main/java/com/corosus/zombieawareness/client/SoundProfileEntry.java similarity index 64% rename from src/main/java/ZombieAwareness/SoundProfileEntry.java rename to src/main/java/com/corosus/zombieawareness/client/SoundProfileEntry.java index bdb0893..ceb8641 100644 --- a/src/main/java/ZombieAwareness/SoundProfileEntry.java +++ b/src/main/java/com/corosus/zombieawareness/client/SoundProfileEntry.java @@ -1,7 +1,9 @@ -package ZombieAwareness; +package com.corosus.zombieawareness.client; -import net.minecraft.util.SoundEvent; -import net.minecraftforge.fml.common.ObfuscationReflectionHelper; +import net.minecraft.sounds.SoundEvent; + +import java.util.ArrayList; +import java.util.List; /** * Accounts for all things we need deal with for sounds, range, amp, chance, matching, etc @@ -13,14 +15,16 @@ public class SoundProfileEntry { //for both partial matches and SoundEvents converted to resource string private String soundName; + private List listSoundEventTypes = new ArrayList<>(); + private boolean isSoundType; private double multiplier; - private double distanceMax = 3D; + private double maxDistToSpawnFromPlayer = 4D; private boolean partialMatchOnly = false; //0 = always use private int oddsTo1ToUse = 0; public static String getSoundEventName(SoundEvent soundEvent) { - return ObfuscationReflectionHelper.getPrivateValue(SoundEvent.class, soundEvent, "field_187506_b").toString(); + return soundEvent.location.toString(); } public SoundProfileEntry(SoundEvent soundEvent, double multiplier/*, double distanceMax*/) { @@ -59,12 +63,12 @@ public void setMultiplier(double multiplier) { this.multiplier = multiplier; } - public double getDistanceMax() { - return distanceMax; + public double getMaxDistToSpawnFromPlayer() { + return maxDistToSpawnFromPlayer; } - public SoundProfileEntry setDistanceMax(double distanceMax) { - this.distanceMax = distanceMax; + public SoundProfileEntry setMaxDistToSpawnFromPlayer(double maxDistToSpawnFromPlayer) { + this.maxDistToSpawnFromPlayer = maxDistToSpawnFromPlayer; return this; } @@ -83,5 +87,26 @@ public int getOddsTo1ToUse() { public void setOddsTo1ToUse(int oddsTo1ToUse) { this.oddsTo1ToUse = oddsTo1ToUse; } - + + public boolean containsSoundType(int soundType) { + return listSoundEventTypes.contains(soundType); + } + + public List getListSoundEventTypes() { + return listSoundEventTypes; + } + + public SoundProfileEntry setListSoundEventTypes(List listSoundEventTypes) { + this.listSoundEventTypes = listSoundEventTypes; + this.isSoundType = true; + return this; + } + + public boolean isSoundType() { + return isSoundType; + } + + public void setSoundType(boolean soundType) { + isSoundType = soundType; + } } diff --git a/src/main/java/com/corosus/zombieawareness/client/SoundRegistry.java b/src/main/java/com/corosus/zombieawareness/client/SoundRegistry.java new file mode 100644 index 0000000..be4fbcf --- /dev/null +++ b/src/main/java/com/corosus/zombieawareness/client/SoundRegistry.java @@ -0,0 +1,47 @@ +package com.corosus.zombieawareness.client; + +import java.util.HashMap; + +import com.corosus.zombieawareness.ZombieAwareness; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.registries.ForgeRegistries; + +public class SoundRegistry { + + private static HashMap lookupStringToEvent = new HashMap<>(); + + @Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD) + public static class RegistryEvents { + @SubscribeEvent(priority = EventPriority.HIGHEST) + public static void onSoundsRegistry(final RegistryEvent.Register event) { + SoundRegistry.init(); + } + } + + public static void init() { + register("alert"); + register("target"); + register("investigate"); + } + + public static void register(String soundPath) { + ResourceLocation resLoc = new ResourceLocation(ZombieAwareness.MODID, soundPath); + SoundEvent event = new SoundEvent(resLoc).setRegistryName(resLoc); + if (lookupStringToEvent.containsKey(soundPath)) { + System.out.println("ZA SOUNDS WARNING: duplicate sound registration for " + soundPath); + } else { + ForgeRegistries.SOUND_EVENTS.register(event); + lookupStringToEvent.put(soundPath, event); + } + } + + public static SoundEvent get(String soundPath) { + return lookupStringToEvent.get(soundPath); + } + +} diff --git a/src/main/java/com/corosus/zombieawareness/config/MobListsConfig.java b/src/main/java/com/corosus/zombieawareness/config/MobListsConfig.java new file mode 100644 index 0000000..da10a5a --- /dev/null +++ b/src/main/java/com/corosus/zombieawareness/config/MobListsConfig.java @@ -0,0 +1,47 @@ +package com.corosus.zombieawareness.config; + +import net.minecraftforge.common.ForgeConfigSpec; +import net.minecraftforge.common.ForgeConfigSpec.Builder; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import com.corosus.zombieawareness.ZombieAwareness; + +import java.util.ArrayList; +import java.util.List; + +@EventBusSubscriber +public class MobListsConfig { + + public static List enhancedMobsDefaults = new ArrayList<>(); + //public static List listEnhancedMobsParsedConfig = new ArrayList<>(); + + static { + String mc = "minecraft:"; + enhancedMobsDefaults.add(mc + "zombie"); + enhancedMobsDefaults.add(mc + "husk"); + enhancedMobsDefaults.add(mc + "creeper"); + enhancedMobsDefaults.add(mc + "skeleton"); + enhancedMobsDefaults.add(mc + "stray"); + enhancedMobsDefaults.add(mc + "witch"); + enhancedMobsDefaults.add(mc + "zombie_villager"); + } + + private static final Builder BUILDER = new Builder(); + + public static final CategoryGeneral GENERAL = new CategoryGeneral(); + + public static final class CategoryGeneral { + + public final ForgeConfigSpec.ConfigValue> enhancedMobs; + + private CategoryGeneral() { + + BUILDER.comment("General mod settings").push("general"); + + enhancedMobs = BUILDER.comment("Mobs enhanced by zombie awareness").defineList("enhancedMobs", enhancedMobsDefaults, + it -> it instanceof String); + + BUILDER.pop(); + } + } + public static final ForgeConfigSpec CONFIG = BUILDER.build(); +} diff --git a/src/main/java/ZombieAwareness/config/ZAConfig.java b/src/main/java/com/corosus/zombieawareness/config/ZAConfig.java similarity index 80% rename from src/main/java/ZombieAwareness/config/ZAConfig.java rename to src/main/java/com/corosus/zombieawareness/config/ZAConfig.java index ae0be4f..0249196 100644 --- a/src/main/java/ZombieAwareness/config/ZAConfig.java +++ b/src/main/java/com/corosus/zombieawareness/config/ZAConfig.java @@ -1,9 +1,10 @@ -package ZombieAwareness.config; +package com.corosus.zombieawareness.config; -import java.io.File; +import com.corosus.zombieawareness.ZombieAwareness; +import com.corosus.modconfig.ConfigComment; +import com.corosus.modconfig.IConfigCategory; -import modconfig.ConfigComment; -import modconfig.IConfigCategory; +import java.io.File; public class ZAConfig implements IConfigCategory { @@ -32,10 +33,16 @@ public class ZAConfig implements IConfigCategory { @ConfigComment("The amount of delay in game ticks between processing mobs with enhanced AI, less is more frequent [performance sensitive]") public static int tickRateAILoop = 5; @ConfigComment("how frequently the mod iterates all players, effects rates, less is more frequent") - public static int tickRatePlayerLoop = 1; + public static int tickRatePlayerLoop = 20; + + @ConfigComment("How long before a mob can move around again to track a light source, or scent, or sound, increase number to reduce performance impact of mod if needed") + public static int tickCooldownBetweenPathfinds = 300; + + @ConfigComment("How likely zombies making noise near you will attract other zombies, higher = less likely, 0 = every time they make a noise") + public static int noisyZombiesReinforceOddsTo1 = 5; public static boolean debugConsole = false; - public static boolean debugConsoleSpawns = false; + /*public static boolean debugConsoleSpawns = false;*/ public static boolean debugConsoleOmniscient = false; public static boolean debugConsoleSuperDetailed = false; @@ -72,7 +79,7 @@ public String getRegistryName() { @Override public String getConfigFileName() { - return "ZombieAwareness" + File.separator + getName(); + return ZombieAwareness.MODID + File.separator + getName(); } @Override diff --git a/src/main/java/ZombieAwareness/config/ZAConfigClient.java b/src/main/java/com/corosus/zombieawareness/config/ZAConfigClient.java similarity index 73% rename from src/main/java/ZombieAwareness/config/ZAConfigClient.java rename to src/main/java/com/corosus/zombieawareness/config/ZAConfigClient.java index 2e313b3..ffb064b 100644 --- a/src/main/java/ZombieAwareness/config/ZAConfigClient.java +++ b/src/main/java/com/corosus/zombieawareness/config/ZAConfigClient.java @@ -1,9 +1,9 @@ -package ZombieAwareness.config; +package com.corosus.zombieawareness.config; import java.io.File; -import modconfig.ConfigComment; -import modconfig.IConfigCategory; +import com.corosus.zombieawareness.ZombieAwareness; +import com.corosus.modconfig.IConfigCategory; public class ZAConfigClient implements IConfigCategory { @@ -24,7 +24,7 @@ public String getRegistryName() { @Override public String getConfigFileName() { - return "ZombieAwareness" + File.separator + getName(); + return ZombieAwareness.MODID + File.separator + getName(); } @Override diff --git a/src/main/java/ZombieAwareness/config/ZAConfigFeatures.java b/src/main/java/com/corosus/zombieawareness/config/ZAConfigFeatures.java similarity index 77% rename from src/main/java/ZombieAwareness/config/ZAConfigFeatures.java rename to src/main/java/com/corosus/zombieawareness/config/ZAConfigFeatures.java index e176c2b..c190452 100644 --- a/src/main/java/ZombieAwareness/config/ZAConfigFeatures.java +++ b/src/main/java/com/corosus/zombieawareness/config/ZAConfigFeatures.java @@ -1,10 +1,10 @@ -package ZombieAwareness.config; +package com.corosus.zombieawareness.config; import java.io.File; -import modconfig.ConfigComment; -import modconfig.IConfigCategory; -import net.minecraftforge.common.ForgeModContainer; +import com.corosus.zombieawareness.ZombieAwareness; +import com.corosus.modconfig.ConfigComment; +import com.corosus.modconfig.IConfigCategory; public class ZAConfigFeatures implements IConfigCategory { @@ -19,7 +19,7 @@ public class ZAConfigFeatures implements IConfigCategory { @ConfigComment("Pistons cause sound sense sources attracting monsters") public static boolean noisyPistons = true; @ConfigComment("Causes monsters to horde up and wander the surface together to random points") - public static boolean wanderingHordes = false; + public static boolean wanderingHordes = false;/* @ConfigComment("Spawn extra zombies randomly on the surface at night if dark and away from player until Spawning.extraSpawningMaxCount is reached") public static boolean extraSpawningSurface = false; @ConfigComment("Spawn extra zombies in caves where other zombies already are if dark and away from player until Spawning.extraSpawningMaxCount is reached") @@ -27,7 +27,10 @@ public class ZAConfigFeatures implements IConfigCategory { @ConfigComment("MC/Forge default is 0.1, this overrides that default, set to -1 to cause no override then restore forges default in their config") public static double zombieSummonHelpBaseChance = 0D; @ConfigComment("MC/Forge default is 0.05, this overrides that default, set to -1 to cause no override then restore forges default in their config") - public static double zombieBabyChance = 0.0F; + public static double zombieBabyChance = 0.0F;*/ + + @ConfigComment("How often in seconds we set a random point around a player for mobs to wander to from far away. Makes mobs path around more, dont use if you have TPS performance issues.") + public static int frequencyOfWanderingHordesPerPlayer = 30; @ConfigComment("How loud sounds should be when you are alerted that a mob is coming to investigate an area near you") public static double soundVolumeInvestigate = 0.5D; @@ -57,7 +60,7 @@ public String getRegistryName() { @Override public String getConfigFileName() { - return "ZombieAwareness" + File.separator + getName(); + return ZombieAwareness.MODID + File.separator + getName(); } @Override @@ -67,8 +70,8 @@ public String getCategory() { @Override public void hookUpdatedValues() { - if (zombieBabyChance != -1) ForgeModContainer.zombieBabyChance = (float) zombieBabyChance; - if (zombieSummonHelpBaseChance != -1) ForgeModContainer.zombieSummonBaseChance = zombieSummonHelpBaseChance; + /*if (zombieBabyChance != -1) ForgeModContainer.zombieBabyChance = (float) zombieBabyChance; + if (zombieSummonHelpBaseChance != -1) ForgeModContainer.zombieSummonBaseChance = zombieSummonHelpBaseChance;*/ } } diff --git a/src/main/java/ZombieAwareness/config/ZAConfigMobLists.java b/src/main/java/com/corosus/zombieawareness/config/ZAConfigMobLists.java similarity index 67% rename from src/main/java/ZombieAwareness/config/ZAConfigMobLists.java rename to src/main/java/com/corosus/zombieawareness/config/ZAConfigMobLists.java index 37c8c9d..679b326 100644 --- a/src/main/java/ZombieAwareness/config/ZAConfigMobLists.java +++ b/src/main/java/com/corosus/zombieawareness/config/ZAConfigMobLists.java @@ -1,7 +1,7 @@ -package ZombieAwareness.config; +package com.corosus.zombieawareness.config; -import modconfig.ConfigComment; -import modconfig.IConfigCategory; +import com.corosus.zombieawareness.ZombieAwareness; +import com.corosus.modconfig.IConfigCategory; import java.io.File; @@ -21,7 +21,7 @@ public String getRegistryName() { @Override public String getConfigFileName() { - return "ZombieAwareness" + File.separator + getName(); + return ZombieAwareness.MODID + File.separator + getName(); } @Override diff --git a/src/main/java/ZombieAwareness/config/ZAConfigPlayerLists.java b/src/main/java/com/corosus/zombieawareness/config/ZAConfigPlayerLists.java similarity index 55% rename from src/main/java/ZombieAwareness/config/ZAConfigPlayerLists.java rename to src/main/java/com/corosus/zombieawareness/config/ZAConfigPlayerLists.java index d304eec..5727f86 100644 --- a/src/main/java/ZombieAwareness/config/ZAConfigPlayerLists.java +++ b/src/main/java/com/corosus/zombieawareness/config/ZAConfigPlayerLists.java @@ -1,9 +1,10 @@ -package ZombieAwareness.config; +package com.corosus.zombieawareness.config; import java.io.File; -import modconfig.ConfigComment; -import modconfig.IConfigCategory; +import com.corosus.zombieawareness.ZombieAwareness; +import com.corosus.modconfig.ConfigComment; +import com.corosus.modconfig.IConfigCategory; public class ZAConfigPlayerLists implements IConfigCategory { @@ -12,20 +13,10 @@ public class ZAConfigPlayerLists implements IConfigCategory { public static boolean whiteListUsedOmniscient = false; @ConfigComment("Uses list of people to have senses spawned for") public static boolean whiteListUsedSenses = false; - /*@ConfigComment("Uses list of mobs to prevent enhanced AI on") - public static boolean blacklistUsedAITick = true; - @ConfigComment("swaps blacklistUsedAITick/blacklistAITick into a whitelist") - public static boolean forceListUsedAITickAsWhitelist = false;*/ - @ConfigComment("Uses list of people to have spawning of zombies for") - public static boolean whiteListUsedExtraSpawning = false; @ConfigComment("List of people to have omniscient targetting effect") public static String whitelistOmniscientTargettedPlayers = "Corosus, SomeDude"; @ConfigComment("List of people to have senses spawned for") public static String whitelistSenses = "Corosus, SomeDude"; - /*@ConfigComment("List of mobs to prevent enhanced AI on") - public static String blacklistAITick = "Creeper, Enderman, Wolf";*/ - @ConfigComment("List of people to have spawning of zombies for") - public static String whitelistExtraSpawning = "Corosus"; @Override public String getName() { @@ -39,7 +30,7 @@ public String getRegistryName() { @Override public String getConfigFileName() { - return "ZombieAwareness" + File.separator + getName(); + return ZombieAwareness.MODID + File.separator + getName(); } @Override diff --git a/src/main/java/ZombieAwareness/config/ZAConfigSpawning.java b/src/main/java/com/corosus/zombieawareness/config/ZAConfigSpawning.java similarity index 92% rename from src/main/java/ZombieAwareness/config/ZAConfigSpawning.java rename to src/main/java/com/corosus/zombieawareness/config/ZAConfigSpawning.java index 5dfd4e4..8ab2f8e 100644 --- a/src/main/java/ZombieAwareness/config/ZAConfigSpawning.java +++ b/src/main/java/com/corosus/zombieawareness/config/ZAConfigSpawning.java @@ -1,9 +1,10 @@ -package ZombieAwareness.config; +package com.corosus.zombieawareness.config; import java.io.File; -import modconfig.ConfigComment; -import modconfig.IConfigCategory; +import com.corosus.zombieawareness.ZombieAwareness; +import com.corosus.modconfig.ConfigComment; +import com.corosus.modconfig.IConfigCategory; public class ZAConfigSpawning implements IConfigCategory { @@ -62,7 +63,7 @@ public String getRegistryName() { @Override public String getConfigFileName() { - return "ZombieAwareness" + File.separator + getName(); + return ZombieAwareness.MODID + File.separator + getName(); } @Override diff --git a/src/main/java/com/corosus/zombieawareness/mixin/MixinLevelEvent.java b/src/main/java/com/corosus/zombieawareness/mixin/MixinLevelEvent.java new file mode 100644 index 0000000..87ae6a4 --- /dev/null +++ b/src/main/java/com/corosus/zombieawareness/mixin/MixinLevelEvent.java @@ -0,0 +1,20 @@ +package com.corosus.zombieawareness.mixin; + +import com.corosus.zombieawareness.ZAUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.player.Player; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerLevel.class) +public abstract class MixinLevelEvent { + + @Inject(method = "levelEvent", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;broadcast(Lnet/minecraft/world/entity/player/Player;DDDDLnet/minecraft/resources/ResourceKey;Lnet/minecraft/network/protocol/Packet;)V")) + public void injectBroadcast(Player p_8684_, int type, BlockPos blockPos, int data, CallbackInfo ci) { + ZAUtil.hookPlayEvent(type, ((ServerLevel)(Object)this), blockPos.getX(), blockPos.getY(), blockPos.getZ(), data); + } +} diff --git a/src/main/java/com/corosus/zombieawareness/mixin/MixinPlaySound.java b/src/main/java/com/corosus/zombieawareness/mixin/MixinPlaySound.java new file mode 100644 index 0000000..728b981 --- /dev/null +++ b/src/main/java/com/corosus/zombieawareness/mixin/MixinPlaySound.java @@ -0,0 +1,21 @@ +package com.corosus.zombieawareness.mixin; + +import com.corosus.zombieawareness.ZAUtil; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.player.Player; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerLevel.class) +public abstract class MixinPlaySound { + + @Inject(method = "playSound(Lnet/minecraft/world/entity/player/Player;DDDLnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;broadcast(Lnet/minecraft/world/entity/player/Player;DDDDLnet/minecraft/resources/ResourceKey;Lnet/minecraft/network/protocol/Packet;)V")) + public void injectBroadcast(Player pExcept, double pX, double pY, double pZ, SoundEvent sound, SoundSource soundSource, float volume, float pitch, CallbackInfo ci) { + ZAUtil.hookSoundEvent(sound, ((ServerLevel)(Object)this), pX, pY, pZ, volume, pitch); + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg new file mode 100644 index 0000000..07db0af --- /dev/null +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -0,0 +1,3 @@ +public-f net.minecraft.network.protocol.game.ClientboundSoundPacket f_133433_ # sound +public-f net.minecraft.network.protocol.game.ClientboundLevelEventPacket f_132258_ # type +public-f net.minecraft.sounds.SoundEvent f_11656_ # location \ No newline at end of file diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..40461f4 --- /dev/null +++ b/src/main/resources/META-INF/mods.toml @@ -0,0 +1,12 @@ +modLoader="javafml" +loaderVersion="[35,)" +license="All Rights Reserved" + +[[mods]] +modId="zombieawareness" +version="${file.jarVersion}" +displayName="Zombie Awareness" +authors="Corosus" +description=''' +Adds sound and smell awareness to mobs +''' \ No newline at end of file diff --git a/src/main/resources/assets/zombieawareness/lang/en_US.lang b/src/main/resources/assets/zombieawareness/lang/en_US.lang deleted file mode 100644 index 3240b04..0000000 --- a/src/main/resources/assets/zombieawareness/lang/en_US.lang +++ /dev/null @@ -1,3 +0,0 @@ -zombieawareness.subtitle.alert=! -zombieawareness.subtitle.target=Mob Alerted -zombieawareness.subtitle.investigate=Mob Investigating \ No newline at end of file diff --git a/src/main/resources/assets/zombieawareness/lang/en_us.json b/src/main/resources/assets/zombieawareness/lang/en_us.json new file mode 100644 index 0000000..3ceaeeb --- /dev/null +++ b/src/main/resources/assets/zombieawareness/lang/en_us.json @@ -0,0 +1,5 @@ +{ + "zombieawareness.subtitle.alert": "!", + "zombieawareness.subtitle.target": "Mob Alerted", + "zombieawareness.subtitle.investigate": "Mob Investigating" +} \ No newline at end of file diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info deleted file mode 100644 index 18961de..0000000 --- a/src/main/resources/mcmod.info +++ /dev/null @@ -1,16 +0,0 @@ -[ -{ - "modid": "zombieawareness", - "name": "Zombie Awareness", - "description": "Smarter more aware zombies (and skeletons), they track you down via blood scent, sound, and light source awareness, config lets you pick and choose what features to have.", - "version": "${version}", - "mcversion": "${mcversion}", - "url": "", - "updateUrl": "", - "authorList": ["Corosus"], - "credits": "", - "logoFile": "", - "screenshots": [], - "dependencies": [] -} -] diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta new file mode 100644 index 0000000..c79a362 --- /dev/null +++ b/src/main/resources/pack.mcmeta @@ -0,0 +1,7 @@ +{ + "pack": { + "description": "examplemod resources", + "pack_format": 6, + "_comment": "A pack_format of 6 requires json lang files and some texture changes from 1.16.2. Note: we require v6 pack meta for all mods." + } +} diff --git a/src/main/resources/zombieawareness.mixins.json b/src/main/resources/zombieawareness.mixins.json new file mode 100644 index 0000000..c319159 --- /dev/null +++ b/src/main/resources/zombieawareness.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "package": "com.corosus.zombieawareness.mixin", + "compatibilityLevel": "JAVA_17", + "refmap": "zombieawareness.refmap.json", + "mixins": [ + "MixinPlaySound", + "MixinLevelEvent" + ], + "injectors": { + "defaultRequire": 1 + }, + "minVersion": "0.8" +}