commit da6b007a3a1ad5f055a2350cd6f2f1b01b10ce15 Author: Electroblob <35599699+Electroblob77@users.noreply.github.com> Date: Fri Jan 19 20:33:04 2018 +0000 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..2c770e09 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# eclipse +bin +*.launch +.settings +.metadata +.classpath +.project + +# idea +out +*.ipr +*.iws +*.iml +.idea + +# gradle +build +.gradle + +# other +eclipse +run diff --git a/CREDITS-fml.txt b/CREDITS-fml.txt new file mode 100644 index 00000000..97122f5d --- /dev/null +++ b/CREDITS-fml.txt @@ -0,0 +1,28 @@ +This is Forge Mod Loader. + +You can find the source code at all times at https://github.com/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-new.txt b/LICENSE-new.txt new file mode 100644 index 00000000..be2c9e66 --- /dev/null +++ b/LICENSE-new.txt @@ -0,0 +1,483 @@ +Minecraft Forge is 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. + +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. + + + 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/MinecraftForge-Credits.txt b/MinecraftForge-Credits.txt new file mode 100644 index 00000000..d0de5a52 --- /dev/null +++ b/MinecraftForge-Credits.txt @@ -0,0 +1,26 @@ +* Eloraam * + +* FlowerChild * + +* Hawkye * + +* MALfunction84 * + +Submitted the sleep handler code for his mod (Somnia) and others to use. + +* Scokeev9 * + +Gave permission for ScotTools API to be integrated into MCF, and also supported the Forge by converting his mods to use it. + +ScotTools Background: ScotTools was an API that enabled modders to add blocks to harvesting levels (and many other ease-of-use features to create new tools), and the first tool API that used block material for block breaking efficiency which allowed blocks from mods that didn't use ScotTools API to break with the correct speed. + +* SpaceToad * + +* LexManos * + +* cpw * + +* 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. diff --git a/Paulscode IBXM Library License.txt b/Paulscode IBXM Library License.txt new file mode 100644 index 00000000..d4884b07 --- /dev/null +++ b/Paulscode IBXM Library License.txt @@ -0,0 +1,10 @@ +IBXM is copyright (c) 2007, Martin Cameron, and is licensed under the BSD License. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of mumart nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Paulscode SoundSystem CodecIBXM License.txt b/Paulscode SoundSystem CodecIBXM License.txt new file mode 100644 index 00000000..a68a4947 --- /dev/null +++ b/Paulscode SoundSystem CodecIBXM License.txt @@ -0,0 +1,40 @@ +SoundSystem CodecIBXM Class License: + +You are free to use this class for any purpose, commercial or otherwise. +You may modify this class or source code, and distribute it any way you +like, provided the following conditions are met: + +1) You may not falsely claim to be the author of this class or any + unmodified portion of it. +2) You may not copyright this class or a modified version of it and then + sue me for copyright infringement. +3) If you modify the source code, you must clearly document the changes + made before redistributing the modified source code, so other users know + it is not the original code. +4) You are not required to give me credit for this class in any derived + work, but if you do, you must also mention my website: + http://www.paulscode.com +5) I the author will not be responsible for any damages (physical, + financial, or otherwise) caused by the use if this class or any + portion of it. +6) I the author do not guarantee, warrant, or make any representations, + either expressed or implied, regarding the use of this class or any + portion of it. + +Author: Paul Lamb +http://www.paulscode.com + + +This software is based on or using the IBXM library available from +http://www.geocities.com/sunet2000/ + + +IBXM is copyright (c) 2007, Martin Cameron, and is licensed under the BSD License. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of mumart nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..30732099 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Wizardry diff --git a/README.txt b/README.txt new file mode 100644 index 00000000..b45931f4 --- /dev/null +++ b/README.txt @@ -0,0 +1,59 @@ +------------------------------------------- +Source installation information for modders +------------------------------------------- +This code follows the Minecraft Forge installation methodology. It will apply +some small patches to the vanilla MCP source code, giving you and it access +to some of the data and functions you need to build a successful mod. + +Note also that the patches are built against "unrenamed" MCP source code (aka +srgnames) - this means that you will not be able to read them directly against +normal code. + +Source pack installation information: + +Standalone source installation +============================== + +Step 1: Open your command-line and browse to the folder where you extracted the zip file. + +Step 2: Once you have a command window up in the folder that the downloaded material was placed, type: + +Windows: "gradlew setupDecompWorkspace" +Linux/Mac OS: "./gradlew setupDecompWorkspace" + +Step 3: After all that finished, you're left with a choice. +For eclipse, run "gradlew eclipse" (./gradlew eclipse if you are on Mac/Linux) + +If you preffer to use IntelliJ, steps are a little different. +1. Open IDEA, and import project. +2. Select your build.gradle file and have it import. +3. Once it's finished you must close IntelliJ and run the following command: + +"gradlew genIntellijRuns" (./gradlew genIntellijRuns if you are on Mac/Linux) + +Step 4: The final step is to open Eclipse and switch your workspace to /eclipse/ (if you use IDEA, it should automatically start on your project) + +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 effect your code} and then start the processs again. + +Should it still not work, +Refer to #ForgeGradle on EsperNet for more information about the gradle environment. + +Tip: +If you do not care about seeing Minecraft's source code you can replace "setupDecompWorkspace" with one of the following: +"setupDevWorkspace": Will patch, deobfusicated, and gather required assets to run minecraft, but will not generated human readable source code. +"setupCIWorkspace": Same as Dev but will not download any assets. This is useful in build servers as it is the fastest because it does the least work. + +Tip: +When using Decomp workspace, the Minecraft source code is NOT added to your workspace in a editable way. Minecraft is treated like a normal Library. Sources are there for documentation and research purposes and usually can be accessed under the 'referenced libraries' section of your IDE. + +Forge source installation +========================= +MinecraftForge ships with this code and installs it as part of the forge +installation process, no further action is required on your part. + +LexManos' Install Video +======================= +https://www.youtube.com/watch?v=8VEdtQLuLO0&feature=youtu.be + +For more details update more often refer to the Forge Forums: +http://www.minecraftforge.net/forum/index.php/topic,14048.0.html diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..ed1e5ab8 --- /dev/null +++ b/build.gradle @@ -0,0 +1,76 @@ +buildscript { + repositories { + jcenter() + maven { url = "http://files.minecraftforge.net/maven" } + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT' + } +} +apply plugin: 'net.minecraftforge.gradle.forge' +//Only edit below this line, the above code adds and enables the nessasary things for Forge to be setup. + + +version = " 2.1.0 - MC 1.10.2" +group= "electroblob.wizardry"// http://maven.apache.org/guides/mini/guide-naming-conventions.html +archivesBaseName = "Electroblob's Wizardry " + +srcCompat = JavaVersion.VERSION_1_8 +targetCompat = JavaVersion.VERSION_1_8 + +minecraft { + version = "1.10.2-12.18.3.2185" + 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 allways work. + // simply re-run your setup task after changing the mappings to update your workspace. + mappings = "snapshot_20161111" + // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. +} + +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... + // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html + // http://www.gradle.org/docs/current/userguide/dependency_management.html + +} + +processResources +{ + // this will ensure that this task is redone when the versions change. + inputs.property "version", project.version + inputs.property "mcversion", project.minecraft.version + + // replace stuff in mcmod.info, nothing else + from(sourceSets.main.resources.srcDirs) { + include 'mcmod.info' + + // replace version and mcversion + expand 'version':project.version, 'mcversion':project.minecraft.version + } + + // copy everything else, thats not the mcmod.info + from(sourceSets.main.resources.srcDirs) { + exclude 'mcmod.info' + } +} diff --git a/forge-1.10.2-12.18.3.2185-changelog.txt b/forge-1.10.2-12.18.3.2185-changelog.txt new file mode 100644 index 00000000..479c2ec5 --- /dev/null +++ b/forge-1.10.2-12.18.3.2185-changelog.txt @@ -0,0 +1,11549 @@ +Changelog: +Build 2185: + LexManos: Bump version number for new Recommended Build. + +Build 1.11-13.19.0.2184: + LexManos: + Prevent a crash when scheduling block updates for non-existing blocks + #3485 + +Build 1.10.2-12.18.2.2183: + LexManos: + Prevent a crash when scheduling block updates for non-existing blocks + #3485 + +Build 1.10.2-12.18.2.2182: + LexManos: + Make Container.mergeItemStack respect target slot's max size when slot + is non-empty. Closes #3498 Fix Shift clicking in Beacons with stacked + items when fuel slot is empty. + (cherry picked from commit c6bc4ec5a92b640ebe773bfa48732dfa7eb59a17) + LexManos: + Fix NPE when rendering missing mod screen with version bound + requirements. #3501 + (cherry picked from commit c1e832bff09c949bfe736f492c2e4aa8004baa84) + LexManos: + Fix issue reading chunks from the network when client and server do not + agree on block count. Closes #34925 + (cherry picked from commit daf21e4dcf246cfa15c362800effc5dc787ab777) + +Build 1.11-13.19.0.2181: + LexManos: Make sure to log kick messages in NetworkDispatcher. Closes #3507 + LexManos: + Fix NPE when rendering missing mod screen with version bound + requirements. #3501 + LexManos: + Added isItemValidForSlot check to hopper extraction (#3444) + (cherry picked from commit 0348760a774ca7ed9639376baadae38e272f5bf8) + LexManos: + Fix for showing config default values twice in tooltip (#2257) (#3338) + (cherry picked from commit be73ec3d5ebd972cfacb0f255f71112a0430bc43) + LexManos: + Add null check and informative error message when client receives an + invalid update packet (#3266) + (cherry picked from commit 882e0e3bf22784d693e3816b0efeba5fbedb1a22) + LexManos: + Add ItemBlockSpecial#getBlock (#3343) + (cherry picked from commit cf26f4acbe48ee8790edc03d0b5c16c1167278ab) + LexManos: + Added gradle.properties to MDK (#3361) + The file sets the default max heap size to 3 GiB so that the + decompilation doesn't fail as often. + (cherry picked from commit 49ec3d1b9322ee2b7a1312059118173b0b3fe6d0) + LexManos: + Make SlotItemHandler override Slot.getSlotStackLimit. Closes #3497 Make + Container.mergeItemStack respect target slot's max size when slot is + non-empty. Closes #3498 Fix Shift clicking in Beacons with stacked items + when fuel slot is empty. + LexManos: Restore missed patch in World.updateComparatorOutputLevel. Closes #3495 + LexManos: + Fix issue reading chunks from the network when client and server do not + agree on block count. Closes #34925 + +Build 1.11-13.19.0.2180: + diesieben07: Add missing pooled BlockPos retain in World patch (#3486) + mezz: + Add a way for modded GuiScreens to cancel mouse and keyboard events + (#3438) + blay09: + [1.11] Add canApplyAtEnchantmentTable to Item (#3463) + Allowing increased control of which enchantments can be put on an item + over Vanilla's hard-coded item type checks. + disabled when the item is enchantable but the enchantability requirement + is not met on lower levels. + mezz: [1.11] Add biome dictionary types for newer biomes (#3475) + bs2609: [1.11] Fix null checks in shouldCauseReequipAnimation (#3482) + +Build 1.10.2-12.18.2.2179: + blay09: + Add null check and informative error message when client receives an + invalid update packet (#3266) + raoulvdberge: Add ItemBlockSpecial#getBlock (#3343) + yannick: + Added gradle.properties to MDK (#3361) + The file sets the default max heap size to 3 GiB so that the + decompilation doesn't fail as often. + +Build 1.11-13.19.0.2178: + liach: Moved TextureStitchEvent.Pre to allow custom sprites for blocks (#3436) + +Build 1.11-13.19.0.2177: + bs2609: Fix sky colour blending not working for render distances >16 (#3476) + +Build 1.11-13.19.0.2176: + logictechcorp: Allow connectable blocks to choose what they can connect to (#3437) + +Build 1.11-13.19.0.2175: + dawn0966: Changed Armor Material to use an ItemStack aware version (#3469) + +Build 1.11-13.19.0.2174: + bs2609: Fix Redstone Torch memory leak (#3474) + +Build 1.11-13.19.0.2173: + diesieben07: BiomeDictionary code cleanup (#3466) + +Build 1.11-13.19.0.2172: + TechnicianLP: Added EquipmentChangeEvent (#3411) + +Build 1.10.2-12.18.2.2171: + bs2609: Fix Redstone Torch memory leak (#3271) + +Build 1.10.2-12.18.2.2170: + 1vInc: Fix for showing config default values twice in tooltip (#2257) (#3338) + +Build 1.11-13.19.0.2169: + marvinroesch99: Update assets to new Forge brand (#3464) + +Build 1.11-13.19.0.2168: + LexManos: + First pass at exposing data fixers to modders. Each mod gets its own + version number, mods SHOULD NOT rely on other mod's fixers, just care + about yourself. Walkers can use IDataFixerData to retrieve their + version. + +Build 1.11-13.19.0.2167: + williewillus: Add getSlotLimit(slot) to IItemHandler (#3445) + +Build 1.10.2-12.18.2.2166: + draco18s: Added isItemValidForSlot check to hopper extraction (#3444) + +Build 1.11-13.19.0.2165: + mezz: Provide access to GuiContainer location and dimensions (#3440) + kashike: + Add method to check if an Entity can trample a Block. Fixes #2510 + (#2730) + cpw: + Pass along if the respawn event was the result of the end being + conquered or not (end respawning is really FUNKY code and uses death + instead of 'change dimension') + +Build 1.11-13.19.0.2162: + bs2609: Clean up handling of changes to IVertexConsumer (#3458) + +Build 1.11-13.19.0.2161: + bs2609: Quick fix for another ItemStack null check (#3426) + mezz: Provide access to ShapedOreRecipe width and height (#3439) + +Build 1.11-13.19.0.2160: + cpw: + Fix problem with the server hanging around after forcing the client to + exit, leaving behind stale processes. + +Build 1.11-13.19.0.2159: + mezz: + Fix #3432 Hopper and Dropper item moving does not exactly match vanilla + (#3442) + mezz: Fix using the wrong Pair implementation (#3449) + +Build 1.11-13.19.0.2157: + mezz: Fix #3424 ItemCraftedEvent returns air when shift-clicking (#3441) + +Build 1.11-13.19.0.2156: + mezz: + Cleanup some null checks and remove deprecated methods in Item and Block + (#3429) + +Build 1.11-13.19.0.2155: + LexManos: Make all tests one side only, so we can test vanilla clients in dev env. + LexManos: + Insert FML packet handler into Vanilla pipelines in case modders send + FMLPacket to vanilla. Fixes #3422 + LexManos: Fix max CustomPayload size, 1MB not 16MB. + +Build 1.11-13.19.0.2154: + LexManos: Fix AbstractMethodException in FML Registries. Closes #3427 + +Build 1.11-13.19.0.2153: + shadowfacts: + Move EntityHorse capability patches to AbstractHorse (#3417) + Fixes #3409 + LexManos: Fix ItemStack null check in PlayerInteractionManager + +Build 1.11-13.19.0.2152: + shadowfacts: Initial annotations/nullability changes (#3392) + TechnicianLP: Remove Block.addInformation exists in vanilla (#3413) + LexManos: Fix Item transfers related to Hoppers. + +Build 1.10.2-12.18.2.2151: + ohai.iChun: + Add GetCollisionBoxesEvent. Allows manipulation of the collision boxes + for moving entities and entities stuck in walls. (#3397) + +Build 1.11-13.19.0.2150: + ohai.iChun: + Add GetCollisionBoxesEvent. Allows manipulation of the collision boxes + of blocks for entities. MC1.11 edition. (#3405) + +Build 1.11-13.19.0.2149: + LexManos: First draft of Entity Registry re-write. + +Build 1.11-13.19.0.2148: + LexManos: + Make Wither Skulls respect Block.canEntityDestroy. Closes #3404 + (cherry picked from commit 6b52023a5c341420d976cb50359e2a2a4a6a9192) + +Build 1.10.2-12.18.2.2147: + LexManos: Make Wither Skulls respect Block.canEntityDestroy. Closes #3404 + +Build 1.11-13.19.0.2146: + Bartek_s2: Added EnumHand parameter to Block.getStateForPlacement (#3402) + +Build 1.11-13.19.0.2145: + LexManos: + Fix issues with ZombieVillagers Closes #3398 1.11 doesn't intertwine + zombie types anymore so our edits to split them out for custom + professions are not needed. + LexManos: Fix client side placement issue on blocks with GUIs. Closes #3399 + +Build 1.11-13.19.0.2144: + ezterry3: + Fix Villagers for 1.11 (#3401) + - Add cartographer registry entry + - Fix textures for the nitwit + +Build 1.11-13.19.0.2143: + mezz: [1.11] Fix nullability issues around ForgeHooks and Capabilities (#3395) + +Build 1.11-13.19.0.2142: + larsgerrits99: + Change OreDictionary over from "null" to the new ItemStack system. + (#3400) + +Build 1.11-13.19.0.2141: + LexManos: + Do not install mod_list.json for optional mods. Classpath scanning is in + so they will still be detected + +Build 1.10.2-12.18.2.2140: + LexManos: + Do not install mod_list.json for optional mods. Classpath scanning is in + so they will still be detected + +Build 1.10.2-12.18.2.2139: + LexManos: Include Mercurius in installer. See Forge Forums for more info. + +Build 1.11-13.19.0.2138: + LexManos: Include Mercurius in installer. See Forge Forums for more info. + +Build 1.11-13.19.0.2137: + LexManos: Restore the ability to register modded TileEntities. + +Build 1.11-13.19.0.2136: + mezz: Remove deprecated Fluid stuff + mezz: Implement fluid item capabilities without item swapping + +Build 1.11-13.19.0.2135: + LexManos: + Make EntityShulker fire EnderTeleportEvent. Closes #3383 + (cherry picked from commit d48985fafaed6de6f09136b999ca8aa44184a7b9) + LexManos: Fixed damage bar color. Closes #3389 + LexManos: Fix ItemStackHandler's constructor ignoring size. Closes #3386 + shadowfacts: Fix crafting causing crash (#3388) Closes #3387 + +Build 1.10.2-12.18.2.2134: + LexManos: Update installer to 1.5, and fix local buildscript issues. + LexManos: Make EntityShulker fire EnderTeleportEvent. Closes #3383 + +Build 1.11-13.19.0.2133: + LexManos: + Added event to allow modification of player visibility to mobs/AI + (#3382) + (cherry picked from commit 0a5ef64d6ea96fe85d7c62107081ced649e6a3e9) + +Build 1.10.2-12.18.2.2132: + maxanier: + Added event to allow modification of player visibility to mobs/AI + (#3382) + +Build 1.11-13.19.0.2131: + LexManos: Apply missed patch to WorldProvider. + mezz: [1.11] Fix initCapabilities for ItemStacks (#3379) + LexManos: Disable capabilities for empty ItemStacks. + LexManos: Restore another missing patch in WorldProvider. + +Build 1.11-13.19.0.2130: + LexManos: + Fix build.gradle problems: Installer missing url.png UploadArchives + erroring on local systems due to missing changelog Adding branch name + improperly. + +Build 1.11-13.19.0.2129-1.11.x: + LexManos: Fix localizations not loading correctly in legacy resource packs. + +Build 1.11-13.19.0.2128-1.11.x: + LexManos: Fix FurnaceRecipy registration. + +Build 1.11-13.19.0.2127-1.11.x: + LexManos: Fixed test cases for 1.11 changes. + +Build 1.10.2-12.18.2.2125: + cpw: Update to 20161111 mcp names + cpw: regenerate entitylivingbase patch + +Build 1.10.2-12.18.2.2124: + mezz: Improve error message from crashes during loading (#3362) + +Build 1.10.2-12.18.2.2123: + draco18s: Added events for crop growth (#3320) + +Build 1.10.2-12.18.2.2122: + maxanier: Add support for custom entity selectors in commands (#3356) + +Build 1.10.2-12.18.2.2121: + latvianmodder: Added PermissionAPI (#3155) + +Build 1.10.2-12.18.2.2120: + Choonster.2010: Add hand argument to BlockEvent.PlaceEvent (#3221) (#3270) + +Build 1.10.2-12.18.2.2119: + jezzadabomb: + Fixes #3237 and #2752. (#3347) + * Fix #3237 by correctly utilising the keepLooking argument. + + * Explain within SimpleNetworkWrapper's javadoc that the execute of the + handler isn't on the main Minecraft thread. + +Build 1.10.2-12.18.2.2118: + draco18s: Made default slot implementation respect inventory stack size limit. + +Build 1.10.2-12.18.2.2117: + aa1ronham: Fixed Block.setBedOccupied ignoring 'occupied' parameter + +Build 1.10.2-12.18.2.2116: + raoulvdberge: + Add Item#getNBTShareTag (#3342) + * Add Item#getNBTShareTag + + * Update comment + +Build 1.10.2-12.18.2.2115: + bonii-xx: + Call Looting Event on player death and loot table drops (#3275) + * Call Looting Event on player death and loot table drops + + * Simplify patches + + * More code cleanup: Replace old usage with compact forgehooks + implementation + +Build 1.10.2-12.18.2.2114: + LexManos: Fix POTENTIAL issue with Texture Stitcher exceeding max size. + +Build 1.10.2-12.18.2.2113: + dawn0966: + Fixed Ore Dictionary replacing the oak boat recipe (#3329) + Fixes the oak boat recipe being able to be crafted with any wood + +Build 1.10.2-12.18.2.2112: + rwtema: + Improve the default isItemValid() and getItemStackLimit() + implementations in SlotItemHandler. You can now properly swap the + player's held itemstack with the slot's itemstack. (#3273) + +Build 1.10.2-12.18.2.2111: + LexManos: + Fixed texture stitcher not using full texture height before expanding + width. + +Build 1.10.2-12.18.2.2110: + LexManos: + Special case not drooping snowballs for water and snow layers. Closes + #3326 + LexManos: Update readme to be a little more clear. + +Build 1.10.2-12.18.2.2109: + bs2609: Fix MC-105406 (OpenGL leak in View Frustum) (#3308) + +Build 1.10.2-12.18.2.2108: + mezz: Fix very laggy mipmap slider MC-64581 (#3305) + +Build 1.10.2-12.18.2.2107: + shadowfacts: Add Block#getStateForPlacement (#3129) + +Build 1.10.2-12.18.2.2106: + mezz: Add better key modifier behavior for GUI contexts (#3307) + +Build 1.10.2-12.18.2.2105: + tterrag1098: Make item repair recipe respect itemstack's getMaxDamage (#3295) + +Build 1.10.2-12.18.2.2104: + LexManos: Fix Tabbing issues with config classes. + LexManos: Fix infinite loop in dragon fight when the dragon is over the void. + +Build 1.10.2-12.18.2.2103: + LexManos: Initial pass a re-working the configuration system. Work in progress. + +Build 1.10.2-12.18.2.2102: + atomicblom+github: Allow all Forge Registries to work with ObjectHolder (#3220) + LexManos: @ObjectHolder will now inject null for dummied objects. Closes #3301 + cpw: + Revert "Another fix for registry substitutions. Closes #3212" + This reverts commit ec2977afc919c0f676f59977e6065148ea635f9e. + cpw: + Fix substitutions being broken after freezing - actually store the + original state in registry copies, not the computed state, which is + wrong because it only sees the sub, not the original. + cpw: + Add in missing test runner for a test. Fixes failing tests on jenkins, + hopefully. + +Build 1.10.2-12.18.2.2099: + LexManos: + Revert commits related to filtering proxy and Automatic Subscriber + annotations. Broke some existing setups, will require a annotation + definition change in 1.11. + +Build 1.10.2-12.18.2.2098: + mezz: Fix proxy injector filtering. (#3300) + +Build 1.10.2-12.18.2.2097: + LexManos: Bump version for new Recommended Build. + +Build 1.10.2-12.18.1.2096: + LexManos: Small Cleanup in example build.gradle. + LexManos: + Filter out proxy injectors for incorrect mods. Prevents mod classes + being initialized early when multiple mods are in the same source. + LexManos: + Introduce IContextSetter for events. This will set the active mod + container for events using the normal EventBus. Fixes improper warnings + from initializing mods using the new Registry events. Modders, you + should not use this as it has many performance implementations and if + abused will slow down the event bus A LOT. ActiveModContainer is not + thread safe. + +Build 1.10.2-12.18.1.2095: + mezz: Fix unbound keybindings are activated by unknown keyboard keys (#3288) + +Build 1.10.2-12.18.1.2094: + mmehnert: + caseSensitiveCustomCategories must be set before loading the + configuration file. (#3269) + Otherwise one set with categories in lowercase letters and on case + sensitive + +Build 1.10.2-12.18.1.2093: + tterrag1098: Fix typo in sky rendering causing tons of BlockPos allocations (#3267) + +Build 1.10.2-12.18.1.2092: + cpw: + Add in registry registration events, new subscription events you can use + to make sure you're registering things at the "best" time. + +Build 1.10.2-12.18.1.2091: + LexManos: + Add support for generic filtering of events. Please DO NOT use this in + performance sensitive environments where you will have tons of things + added to the listener list. If that's the case define sub-classes as + they have completely separate lists and will thus be more efficient when + firing the event. + +Build 1.10.2-12.18.1.2090: + lukas.tenbrink: + Fix roofed forests posting decoration events for small mushrooms rather + than big (#3259) + +Build 1.10.2-12.18.1.2089: + mattmess1221: + Add support for offhand bow aiming animation (#3085) + Vanilla allows you to use a bow in your offhand, but it's not visible to + others or in third person. + +Build 1.10.2-12.18.1.2088: + tehgeek: + Remove oredict for Bone Block recipe, stop white dye to bonemeal exploit + (#3255) + +Build 1.10.2-12.18.1.2087: + LexManos: + Fixed Biome.platFlower not being called when bonemealing grass. Closes + #3254 + +Build 1.10.2-12.18.1.2086: + latvianmodder: + Added getCommandMap() & getSortedCommandList() in CommandTreeBase + (#3251) + +Build 1.10.2-12.18.1.2085: + atomicblom+github: New event to allow mutating a baby entity based on it's parents (#3227) + +Build 1.10.2-12.18.1.2084: + latvianmodder: Added CommandTreeBase (#3193) + +Build 1.10.2-12.18.1.2083: + paul.fulham0: Fix debug command creating empty file (MC-103399) (#3218) + +Build 1.10.2-12.18.1.2082: + marvinroesch99: Fix universal bucket not being returned in crafting recipes (#3234) + +Build 1.10.2-12.18.1.2081: + cpw: Create EnergySystems.md + +Build 1.10.2-12.18.1.2080: + LexManos: + Introducing a new standardized energy capability system. Heavily + inspired by the old RedstoneFlux API developed by King Lemming and Team + CoFH. + LexManos: Fix potential shading issue in GuiEditArrayEntries. Closes #3247 + +Build 1.10.2-12.18.1.2079: + LexManos: Fixed ItemStacks having ForgeCaps tag when no caps are serialized. + +Build 1.10.2-12.18.1.2078: + LexManos: Fix Cactus being able top be planted on incorrect soil. + +Build 1.10.2-12.18.1.2077: + bloodmc: Fix wrong var being used during getMaxSpawnPackSize check. (#3240) + +Build 1.10.2-12.18.1.2076: + LexManos: + Move dismount version of EntityMountEvent to Entity class. Should fix + canceling dismounting. Closes #3226 + +Build 1.10.2-12.18.1.2075: + LexManos: Yet another fix for stupid stairs. Closes #3224 + +Build 1.10.2-12.18.1.2074: + LexManos: Make StructureBlock TESRs always render. Closes #3222 + +Build 1.10.2-12.18.1.2073: + paul.fulham0: Fix item use stats (#3216) + +Build 1.10.2-12.18.1.2072: + LexManos: + Fixed decorate event being called with SHROOM type for both convered + trees and large mushrooms. Closes #3215 + +Build 1.10.2-12.18.1.2071: + LexManos: Another fix for registry substitutions. Closes #3212 + +Build 1.10.2-12.18.1.2070: + LexManos: Add legacy support for signature change of AddCallback. + LexManos: + Update gradle wrapper to 2.14, reference MC json, and add java6 compat + lines to default MDK build.gradle. + +Build 1.10.2-12.18.1.2069: + bonii-xx: Revert changes to ItemTool for binary compatibility (#3211) + +Build 1.10.2-12.18.1.2068: + bonii-xx: Expand getHarvestLevel to include player and blockstate (#3192) + +Build 1.10.2-12.18.1.2067: + LexManos: + Make enchantment tables spawn particles for any custom energy provider. + Closes #3208 + +Build 1.10.2-12.18.1.2066: + williewillus: Allow blocks to affect the beacon color (#3205) + +Build 1.10.2-12.18.1.2065: + LexManos: Fix armors rendering badly. Closes #3196 + +Build 1.10.2-12.18.1.2064: + abab9579: Fixed NPE from missing Capabilities (#3191) + +Build 1.10.2-12.18.1.2063: + LexManos: Fix World Capabilities NPE when there are no attached capabilities. + +Build 1.10.2-12.18.1.2062: + abab9579: Introduced Capabilities for World (#3069) + +Build 1.10.2-12.18.1.2061: + LexManos: + Fix vanilla bug in BlockFalling that caused blocks to loose state info + during world gen. Closes #3183 + +Build 1.10.2-12.18.1.2060: + Gerard Bruwn: Added PlayerBrewedPotionEvent (#3187) + +Build 1.10.2-12.18.1.2059: + cpw: Don't crash if you select a fake player + +Build 1.10.2-12.18.1.2058: + LexManos: + Skip searching for mods in the JAVA_HOME directory. Closes #2249 and + #2250 + +Build 1.10.2-12.18.1.2057: + LexManos: + Fix leather armor overlay not rendering when armor is pure white. Closes + #3184 + +Build 1.10.2-12.18.1.2056: + josephcsible: + Fix test mod IDs, names, and versions (#3122) + Most of our test mods have bad IDs (uppercase) and are missing names or + versions. Forge produces a bunch of warnings in the console about this, + which + +Build 1.10.2-12.18.1.2055: + florian: + Make InventoryHelper#spawnItemStack use ItemStack#splitStack to avoid + losing capability data. (#3163) + +Build 1.10.2-12.18.1.2054: + sebastian: + Unlike BakedQuad, UnpackedBakedQuad does not propagate the diffuse + lighting flag to Vertex Consumers. Made UnpackedBakedQuad conform to + BakedQuad's behavior. (#3181) + +Build 1.10.2-12.18.1.2053: + LexManos: + Set ModelLoader.WHITE's texture size iniatlly instead of defaulting to + zero and being set later. + LexManos: Add RenderTooltipEvent (#3034) + +Build 1.10.2-12.18.1.2052: + tehgeek: + Fix #3165 Dispensers with buckets can destroy fluid handler blocks + (#3180) + +Build 1.9.4-12.17.0.2051: + tterrag1098: Add RenderTooltipEvent (#3034) + +Build 1.10.2-12.18.1.2050: + LexManos: Fix Fire not using getFireSpreadSpeed. Closes #3175 + +Build 1.10.2-12.18.1.2049: + LexManos: Fix issue with stairs. + +Build 1.10.2-12.18.1.2048: + LexManos: Removed unintentional functionality when using the Splash Screen. + +Build 1.10.2-12.18.1.2047: + LexManos: Make stair culling use actual state, and be more robust. Closes #3170 + LexManos: Fix name in legacy support in registry changes. + LexManos: + Compleetly skip FML handshake for Vanilla connections. Should fix race + condition. Closes #3084 + LexManos: + Disable DecorateEventDebug by default. Prevents me thinking that + decoration is broken >.< + +Build 1.10.2-12.18.1.2046: + AlgorithmX2: + Fix Vertex Transformer for breaking models (#3132) + * Add setTexture to IVertexConsumer and propagate it. + +Build 1.10.2-12.18.1.2045: + LexManos: + Restone binary compatibility with registry substitution rework. Modders + please switch to using the provided RegistryBuilder to ensure more + forward compatbility. Closes #3144 + LexManos: Fix Forge fluids not serializing properly. Closes #3152 + +Build 1.10.2-12.18.1.2044: + LexManos: + Second pass at fixing 'Dissapearing Doors' This now more robustly fixes + ALL uses of get and has slight performance improvements for itterable + states. Closes #3139 + +Build 1.10.2-12.18.1.2043: + LexManos: + Added config option to disable Forge's fix of Stair/Slab face culling. + Some vanilla resource packs exploit this issue in their custom models + causing unintended rendering issues. + +Build 1.10.2-12.18.1.2042: + LexManos: + Fixed 'Dissapearing Doors', An issue where the world would try to save + blockstates that are not serializeable. + +Build 1.10.2-12.18.1.2041: + cpw: Allow CommandEvent to change the parameter set. Closes #1762 + +Build 1.10.2-12.18.1.2040: + cpw: + Another pass at substitutions - now capable of fixing up ItemBlock to + point at the right block when a block is substituted, should also handle + substituting itemblocks. There's some hackery in the way we have to + change the value of ItemBlock.block, but it's not too egregious in my + opinion. + +Build 1.10.2-12.18.1.2039: + dakotapierone: Update decorate for biomes to support Decorate event + cpw: Clean up AT file + +Build 1.10.2-12.18.1.2038: + cpw: + Fix anvil output slots being wrong by adding new ones. Closes #3121 + without breaking existing mod workarounds for the badly ordered slots. + +Build 1.10.2-12.18.1.2037: + vincent.lee: + Expose vanilla brewing recipe registration (cherry picked from commit + 24bf163) + +Build 1.10.2-12.18.1.2036: + cpw: + Clean up some legacy settings, making sure there's translation strings. + Closes #2965 Thanks for the prompt Zaggy! + +Build 1.10.2-12.18.1.2035: + vincent.lee: Fix MC-91728 (cherry picked from commit 71f75bf) + cpw: Refresh patch after cherry pick (cherry picked from commit 71f75bf) + +Build 1.10.2-12.18.1.2034: + vincent.lee: + Remove sideonly on all other SoundType methods (cherry picked from + commit 46c7cac) + +Build 1.10.2-12.18.1.2033: + mnmiller1: + Fixed NeighborNotifyEvent not containing the piston facing direction. + (cherry picked from commit 5637c41) + +Build 1.10.2-12.18.1.2032: + AlgorithmX2: Use extended state for breaking models (#3086) + +Build 1.10.2-12.18.1.2031: + vincent.lee: No-nonsense getSoundType + cpw: Clean up block patch. Also push json update. + +Build 1.10.2-12.18.1.2030: + Creative-Multimedia-Design: Fixed sound paused/resume issue + +Build 1.10.2-12.18.1.2029: + alexandre.duponchel: FIX : IndexOutOfBoundException + +Build 1.10.2-12.18.1.2028: + cpw: Mark it for actual removal in 1.11 + cpw: Tidy up some more todos + cpw: + Fix #3126 - duplicate stat id error - don't use the "universal + iterators" in statbuilding, use the one without the duplicate entries. + (The duplicate entry one is for rendering setup) + +Build 1.10.2-12.18.1.2027: + cpw: + Fix a vanilla bug with the blockstate ID map not being properly + symmetric with respect to block.getStateFromMeta - closes #3012 properly + but probably has a random MCJIRA ticket somewhere too. + +Build 1.10.2-12.18.1.2026: + vincent.lee: Add ability for custom detector rail output + vincent.lee: Move to a hook in EntityMinecart + vincent.lee: Remove event stuff + +Build 1.10.2-12.18.1.2025: + mezz: Force mipmap even if textures are the wrong size + mezz: Add TextureDump forge test mod + +Build 1.10.2-12.18.1.2024: + vincent.lee: Add hand-specific render event + vincent.lee: Add missing cancelable + cpw: Rename the event to RenderSpecificHandEvent + +Build 1.10.2-12.18.1.2023: + josephcsible: Add CreateFluidSourceEvent to control infinite fluid sources + josephcsible: Add a test for CreateFluidSourceEvent + josephcsible: Use CreateFluidSourceEvent for mod fluids as well + +Build 1.10.2-12.18.1.2022: + mezz: Fix hotkey with default modifier loading wrong when modifier set to NONE + +Build 1.10.2-12.18.1.2021: + bernhard.bonigl: + Add shouldCauseBlockBreakReset callback to Item. This allows to keep + breaking blocks if the NBT or similar changes. + +Build 1.10.2-12.18.1.2020: + Mumfrey: Store member field mappings in the mapping table, possible fix for #3043 + +Build 1.10.2-12.18.1.2019: + cpw: + Something that needs to happen: warn people about bad modids- if they're + not lowercased, or if they're too long. + +Build 1.10.2-12.18.1.2018: + ezterry3: + Fix async race condition on new chunks (closes #3020) (#3090) + This patch fixes a race condition that sometimes caused a just generated + chunk + structures to + generated version + chunk. + This patch ensures when ProvideChunk calls LoadChunk synchronously we + always + condition without + already saved to disk. + Thus this should retain the async functionality from PR #2946 while + preventing + +Build 1.10.2-12.18.1.2017: + cpw: + Some test harness stuffs. Time to get the registry manager properly + tested. + cpw: + More tests. Substitutions now work, and a fix is included. *sigh* Closes + too many bugs to count. + cpw: Some more substitution tests. It seems like it's working as it should. + cpw: Some dummy block replacement tests - it seems they work correctly. + cpw: Add the new tests into the suite + cpw: + Some tweaks to ContainedDeps - it should extract to a file in + versionedMods directly, even if the tag in the jar has a subpath + element. It'll also skip if there is a matching filename in the main + mods dir. + +Build 1.10.2-12.18.1.2016-failtests: + cpw: Some more substitution tests. It seems like it's working as it should. + cpw: Some dummy block replacement tests - it seems they work correctly. + +Build 1.10.2-12.18.1.2014: + LexManos: + Added support for using static methods as event listeners, Register the + .class instead of an instance. And added an improvement to calltime + listener rebuilding. + +Build 1.10.2-12.18.1.2013: + cpw: Lets make a half-decent readme. Thanks to Rorax for the install video! + +Build 1.10.2-12.18.1.2012: + mezz: Add Forge fluid handler test + +Build 1.10.2-12.18.1.2011: + LexManos: Bump version for a RB + +Build 1.10.2-12.18.0.2010: + bernhard.bonigl: + Add an event that allows to modify the looting level based on damage + source + +Build 1.10.2-12.18.0.2009: + williewillus: Add throwable impact event (#3071) + +Build 1.10.2-12.18.0.2007-1.10.0: + LexManos: + Fix hopper extract behavior being broken on edge cases due to + IItemHandler capability. Closes #3062 + +Build 1.10.2-12.18.0.2006-1.10.0: + LexManos: + Fixed FPS graph and new debug lines not being rendered in Debug screen. + Closes #3054 + +Build 1.10.2-12.18.0.2005-1.10.0: + diesieben07: + Restore (buggy?) behavior of vanilla BlockPistonMoving and ignore drop + chance (#3052) + +Build 1.10.2-12.18.0.2004-1.10.0: + LexManos: + Fix villages generating to high when using a cusom sea level. Closes + #3050 + +Build 1.10.2-12.18.0.2003-1.10.0: + LexManos: + Forge will now load extra mods from mods/mod_list.json and + mods/mc_version/mod_list.json. As defined by the same json spec as + --modListFile. And now if repositoryRoot is prefixed with absolute: the + path will NOT be relative to the MC directory. + LexManos: + Update build.gradle to have single line for specifying minecraft + version, and will update ForgeVersion.java as needed. + +Build 1.10.2-12.18.0.2002-1.10.0: + LexManos: + Fixed MC version number... I could of SWORN we removed this in favor of + a automated system... + +Build 1.10.2-12.18.0.2001-1.10.0: + LexManos: 1.10.2 Update + +Build 1.10-12.18.0.2000-1.10.0: + diesieben07: + Fix mob-spawners crashing for non-living entities and simply the patch + in the process (#3042) + +Build 1.10-12.18.0.1999-1.10.0: + mezz: + Fix legacy handling for ItemBucket subclasses and non-universal buckets + (#3038) + mezz: + Fix milk bucket is fillable with water or lava when milk is unregistered + (#3039) + +Build 1.10-12.18.0.1998-1.10.0: + mezz: Remove duplicated universal buckets from the creative list (#3035) + mezz: Fix FluidContainerRegistry handling potions as water bottles (#3037) + +Build 1.10-12.18.0.1997-1.10.0: + cpw: + Big warning for recipe types found without being registered, it turns + out this is pretty severe, modders need to fix it. + +Build 1.10-12.18.0.1996-1.10.0: + mezz: + Fix comparison stability of the Recipe Sorter with unknown recipes #2962 + (#3030) + +Build 1.10-12.18.0.1995-1.10.0: + LexManos: Fixed Husks rendering as standard zombies. Closes #3028 + +Build 1.10-12.18.0.1994-1.10.0: + LexManos: Fixed chests being replaced when trees grew. + +Build 1.10-12.18.0.1993-1.10.0: + luacs1998: Simple implementation of a Open/Close container event (#3017) + +Build 1.10-12.18.0.1991-1.10.0: + LexManos: + Fixed shifted patch in GameSettings causing settings to not load + properly. Closes #3025 #3022 + LexManos: Fixed incorrect variable being sent to doSpecialSpawn. Closes #2986 + LexManos: Fix incorrect logic in ChunkCache.isSideSolid. Closes #3026 + LexManos: Provide 'cleaner' version of TextureMap.setTextureEntry. Closes #1385 + +Build 1.9.4-12.17.0.1990: + LexManos: Fixed incorrect variable being sent to doSpecialSpawn. Closes #2986 + LexManos: Fix incorrect logic in ChunkCache.isSideSolid. Closes #3026 + +Build 1.10-12.18.0.1989-1.10.0: + cpw: Fix doors popping off solid sides. + +Build 1.10-12.18.0.1988-1.10.0: + cpw: Fix that the Missing blocks/items/registries prompt often got stuck. + +Build 1.9.4-12.17.0.1987: + LexManos: + Set ModelLoader.WHITE's texture size iniatlly instead of defaulting to + zero and being set later. + +Build 1.10-12.18.0.1986-1.10.0: + cpw: Fix Realms button text to be shorter again. Closes #3019 + +Build 1.10-12.18.0.1985-1.10.0: + cpw: Fix item pickup dupe bugs. Closes #3015 + +Build 1.10-12.18.0.1984-1.10.0: + blay09: + [1.10] Fix keys with KeyModifier failing to load (#3018) + Fix key options with KeyModifier failing to load due to + NumberFormatException + +Build 1.10-12.18.0.1983-1.10.0: + cpw: + Accept more 1.9.4 version strings. Fix NPE for custom villager types and + their associated zombie spawns. Still needs review. + +Build 1.10-12.18.0.1982-1.10.0: + mezz: Fix all guis closing as soon as they are opened (#3013) + +Build 1.10-12.18.0.1981-1.10.0: + cpw: Update to 1.10 + LexManos: Fix version number. + +Build 1.9.4-12.17.0.1980-1.10.0: + cpw: Fix the MDK - it gets the license too! + +Build 1.9.4-12.17.0.1979-1.10.0: + cpw: + Update the build.gradle script to ship the new license and stop shipping + the old licenses. + +Build 1.9.4-12.17.0.1978-1.10.0: + cpw: + In accordance with our stated goal of relicensing Forge to LGPL v2.1, + this commit enacts that change. Although it is using the 1.9.4 codebase, + it is intended that this branch become public with the 1.10 changes, as + such the license will take effect at that time. The three commits from + persons who have failed to accept the new license, as documented in + #2789, have had their commits reverted. This should complete the license + transition. Commits after the commit date of this commit should be + considered licensed by LGPLv2.1, as indicated in LICENSE-new.txt. All + patches are now considered owned by the Forge project and Forge + Development LLC in particular (note that they will not get the license + boilerplate, as they are machine generated files). + +Build 1.9.4-12.17.0.1976: + GirafiStudios: + Made forge-type spawn eggs spawn childs, when right clicking + EntityAgeable again. (#2982) + +Build 1.9.4-12.17.0.1975: + shadowfacts: Fix broken EntityItem merging for Items with capabilities + diesieben07: + Make ASMDataTable more useful: (#2911) + - EnumHolder now has getters for it's data + - enum arrays work now + - nested annotations work now outside of being in arrays + (cherry picked from commit f10f750) + +Build 1.9.4-12.17.0.1974-1.9.4: + diesieben07: + Make ASMDataTable more useful: (#2911) + - EnumHolder now has getters for it's data + - enum arrays work now + - nested annotations work now outside of being in arrays + +Build 1.9.4-12.17.0.1973: + mezz: Fix incorrect FluidUtil doc (#2996) + +Build 1.9.4-12.17.0.1972: + mezz: Fix ItemStack deserialization can't clear nbt (#3005) + mezz: Improve the "missing mods" on-screen error message (#2997) + +Build 1.9.4-12.17.0.1970: + cpw: Update CONTRIBUTING.md + +Build 1.9.4-12.17.0.1969: + cpw: Update CONTRIBUTING.md + +Build 1.9.4-12.17.0.1968: + mezz: Fix #2918 Make dispensers with buckets work with modded fluids (#2947) + +Build 1.9.4-12.17.0.1967: + LexManos: Fixed brewing stand returning incorrect solts for side inventories. + +Build 1.9.4-12.17.0.1966: + LexManos: Fixed villager career display names being off by one. + +Build 1.9.4-12.17.0.1965: + diesieben07: + Fix DataParameter mixup when vanilla clients are connected causing CCE + in zombie renderer (#2992) + +Build 1.9.4-12.17.0.1964: + williewillus: Fix AnimationItemOverrideList not falling back to super (#2990) + +Build 1.9.4-12.17.0.1963: + mezz: + Fix #2961 Capability data is not preserved by fluid handler interactions + (#2976) + +Build 1.9.4-12.17.0.1962: + cpw: Slight tweak to early loading, to accommodate FMP + +Build 1.9.4-12.17.0.1961: + LexManos: + Fixed Fernflower decompile issue in AnvilChunkLoader.saveExtraData() + Closes #2985 + +Build 1.9.4-12.17.0.1960: + cpw: + Mute the connection reset by peer exception - it's spamilicious for the + most part. + +Build 1.9.4-12.17.0.1959: + cpw: Fix missing IMC message query. + +Build 1.9.4-12.17.0.1958: + man.of.j: Read a Update JSON in UTF-8 (#2978) + +Build 1.9.4-12.17.0.1957: + williewillus: Add getter for the world to LootContext (#2954) + +Build 1.9.4-12.17.0.1956: + gurreja: Fixed arrows not showing in non default AchievementPages (#2968) + +Build 1.9.4-12.17.0.1955: + mezz: Add methods to Fluid to allow custom vaporization overrides (#2959) + +Build 1.9.4-12.17.0.1954: + fry: Fixes #1984. + +Build 1.9.4-12.17.0.1953: + fry: + Modified SimpleModelState to ignore the parts it doesn't know about. + Fixes model part hiding always applying to some models when + transformations are specified in the blockstate json. + +Build 1.9.4-12.17.0.1952: + fry: + Pass the GL context back from the splash screen during the + EntityRenderer init. + +Build 1.9.4-12.17.0.1951: + cpw: + Fire ChunkWatchEvent.Watch event, closes #2767. Based on @sfPlayer1 + patch suggestion. It looks like this logic has shifted relative to 1.8, + so there may be differences in event behaviour now. + +Build 1.9.4-12.17.0.1950: + minecrell: + Fix asynchronous chunk loading (#2946) + Since the update to Minecraft 1.9.4 chunks were actually never loaded + asynchronously because a sync request was always made from the + PlayerChunkMap shortly after the chunk had been queued. + + - PlayerChunkMapEntry now only loads chunks synchronously *after* the + chunk failed to load asynchronously. + - Fixed some minor bugs that caused "Attempted to dequeue chunk" + messages + - Simplified ChunkProviderServer patch. loadChunk no longer generates + chunks, + so there is no need to handle that. + - Moved loader and provider to ChunkIOProvider so there is no need for + "hashCode abuse" + +Build 1.9.4-12.17.0.1949: + mezz: + Make it easier to use FluidTanks that have input/output restrictions + (#2945) + These internal function should NOT be used by anything except the owner + of the tank. + +Build 1.9.4-12.17.0.1948: + LexManos: Fixed BiomeProvider.getModdedBiomeGenerators method name. Closes #2937 + +Build 1.9.4-12.17.0.1947: + williewillus: + Several bugfixes for PlayerInteractEvent (#2943) + * Don't change Result if setCanceled(false) called + + * Fix RightClickEmpty firing even when targeting a block or entity + + * Add a left click empty event + + * Move hook point for EntityInteractSpecific to the right place + + * Fix LeftClickBlock not firing clientside in creative, and clarify docs + about creative + +Build 1.9.4-12.17.0.1946: + mezz: + Add new IFluidHandler for capabilities. (#2942) + Old fluid system is deprecated and slated for removal in the next + cleanup cycle. + +Build 1.9.4-12.17.0.1945: + matti.j.ruohonen: + Add a Slot#isSameInventory() method (#2884) + This fixes shift + double clicking items in Containers that use + SlotItemHandler slots. + +Build 1.9.4-12.17.0.1944: + LexManos: + Fixed POTENTIAL issue where mods would query world infrom from TEs + before they are set. Closes #2863 + +Build 1.9.4-12.17.0.1943: + LexManos: + Fixed client side ticking TileEntites after they are unloaded. Closes + #2852 + LexManos: + Expanded custom villagers to Zombies and world gen. Fixed edge cases + where custom professiosn wernt being used correctly. Closes #2862 + +Build 1.9-12.16.0.1942-1.9: + ljfa-ag: Fix forge:default-tool transformation (#2795) + +Build 1.9.4-12.17.0.1941: + LexManos: Fixed position sent to sanSustainPlant from cactus. Closes #2941 + +Build 1.9.4-12.17.0.1940: + LexManos: + Forge will now warn the user and ask permission to load a world with + unknown registries. Closes #2934 + +Build 1.9.4-12.17.0.1939: + LexManos: Fix BlockFluidBase having collision. Closes #2932 + +Build 1.9.4-12.17.0.1937: + LexManos: + 1.9.4 Update. Major things to note: Class renames: + https://gist.github.com/LexManos/44dd211f90f498ad4015279b103dff86 Tile + Entities are now packed in the ChunkData packet. Forge intends to work + around this to better support large moded worlds, but for the time being + modders should implement the new function carefully and only send what + data they need to! Minecraft's codebase now has annotations, these are + directly from Mojang and should be adheared to! Added support for + package-info.java's containing @Nullable information for all MC code + base. + LexManos: Update mappoings snapshot to 20160518 + cpw: Fix schizophrenia in version numbers. + LexManos: Bump version information for 1.9.4. + LexManos: Update example mod for new mappings. + atomicblom: Add Unit Test to detect mismatches between EnumHelper and Enum ctor + cpw: + Tweak build file slightly - allow passing buildNumber using gradle + parameters (-PbuildNumber=...) and provide a "resetBuildNumber" function + to reset the buildNumber to zero in the ForgeVersion file. Means builds + can be reproducible in-IDE. + tterrag1098: Fix screenshot link paths on Windows (#2886) + LexManos: + Take capabilities into account when comparing ItemStack NBT's. Closes + #2726 + LexManos: + Tipped arrows are now not affected by infinite enchantment but are still + effected by creative mode. + LexManos: Set max enchantment ID to Short.MAX_VALUE. Closes #2799 + LexManos: + Fix Container.compouteStackSize not respecting stack version of + maxStackSize. Closes #2756 + LexManos: Fire playerDestroyItem event's in some cases where we were not. + LexManos: Fix villagers having farmer skin on vanilla servers. Closes #2894 + LexManos: Add alternative ItemAxe Constructor + LexManos: Fix paramters for addArmorMaterial, addOption and addGameType. + LexManos: + Make unit test for EnumHelper more robust and less spammy. Now detects + missing accessors as well. + the.f1repl4ce: Switch new constructor to use floats (#2904) + tterrag1098: Add hook for TE data sync on client (#2893) + cpw: + Strip Optional from the class signature as well, since that's loaded by + the TypeToken registry code and might cause a crash. + LexManos: + Fix installer downloading vanilla jar when not needed. And filter some + more known libraries. + LexManos: + Capture interfaces when scanning classes and preload implementations of + ICrashReportDetail. + LexManos: Set Enchantment table's harvest level. Closes #1349 + LexManos: + More uniformly apply Configuration.caseSensitiveCustomCategories. Closes + #1353 + LexManos: + Close player's inventory to prevent potential dupe issues on some modded + items. Closes #1431 + LexManos: + Move getArmorModel hook up two lines so that the attributes and + animation information is set. Closes #1939 + LexManos: Fire PopulateChunkEvent Pre and Post in ChunkProviderFlat. Closes #2050 + LexManos: Fire LivingSpawnEvents for MobSpawners. Closes #2079 + LexManos: Fix javadocs for BiomeColor events. Closes #2093 + LexManos: + Fixed EntityRegistry.addSpawn adding duplicate spawn entries. Closes + #2112 + LexManos: + Fix setTileEntity causing the world to remove the new and old tile + entities. Closes #2164 + LexManos: Add ServerHangWatchdog to TerminalTransformer whitelist. Closes #2203 + LexManos: Fixed NBTTagString.toString not properly escaping \'s. Closes #2393 + LexManos: + Reinitalize statistics information on Mapping changes. Fixes mismatched + stats to item/block ids. And auto registers stats for modded items. + Closes #2454 + LexManos: + Fix Thorns enchantment bypassing ISpecialArmor.damageArmor function. + Closes #2463 + LexManos: + Fix tooltip for survival tab in the creative menu not drawing on any but + the first page. Closes #2908 and #2504 + LexManos: + Make sure tool-classes for vanilla tools are initialized early. Closes + #2909 and #2525 + LexManos: + Fixed FluidUtil.tryEmptyFluidContainerItem not checking if the tank can + accept fluids first. Closes #2527 + LexManos: + Fixed potential issue with Chunk.getLightOpacity being called before the + chunk is added to the world map. Closes #2529 + LexManos: + Fixed TileEntityBeacon not using position-aware getLightOpacity. Closes + #2530 and #2910 + LexManos: Fixed village wells not having their biomes set. Closes #2499 + LexManos: + Fixed issue with @ObjectHolder and the new registry system. Closes #2640 + Only run static initalizers for classes we need to get the names from + values. + LexManos: + Fixed new ICrashReportDetail loader working with packaged mods by + delaying it until the ModContainer's construct event. Closes #2924 + fry: Reordered transformations during LayerHeldItem rendering, fixes #2827. + fry: + Initial support for model visibility system. Implemented it for OBJ, B3D + and normal vanilla models; fixed NPE accessing parent state in B3DModel. + Fixes #2801. + +Build 1.9.4-12.17.0.1935-1.9.4: + fry: Initial support for model visibility system. Implemented it for OBJ, B3D and normal vanilla models; fixed NPE accessing parent state in B3DModel. Fixes #2801. + +Build 1.9-12.16.1.1934: + mezz: + Improve JavaDoc links, fix some broken JavaDocs (#2833) + + Improve JavaDoc links, fix broken JavaDocs + +Build 1.9.4-12.17.0.1933-1.9.4: + fry: Reordered transformations during LayerHeldItem rendering, fixes #2827. + +Build 1.9.4-12.17.0.1932-1.9.4: + LexManos: Fixed new ICrashReportDetail loader working with packaged mods by delaying it until the ModContainer's construct event. Closes #2924 + +Build 1.9.4-12.17.0.1931-1.9.4: + LexManos: Fixed village wells not having their biomes set. Closes #2499 + LexManos: + Fixed issue with @ObjectHolder and the new registry system. Closes #2640 + Only run static initalizers for classes we need to get the names from values. + +Build 1.9.4-12.17.0.1930-1.9.4: + LexManos: Fix tooltip for survival tab in the creative menu not drawing on any but the first page. Closes #2908 and #2504 + LexManos: Make sure tool-classes for vanilla tools are initialized early. Closes #2909 and #2525 + LexManos: Fixed FluidUtil.tryEmptyFluidContainerItem not checking if the tank can accept fluids first. Closes #2527 + LexManos: Fixed potential issue with Chunk.getLightOpacity being called before the chunk is added to the world map. Closes #2529 + LexManos: Fixed TileEntityBeacon not using position-aware getLightOpacity. Closes #2530 and #2910 + +Build 1.9.4-12.17.0.1929-1.9.4: + LexManos: Fix Thorns enchantment bypassing ISpecialArmor.damageArmor function. Closes #2463 + +Build 1.9.4-12.17.0.1928-1.9.4: + LexManos: Fixed NBTTagString.toString not properly escaping \'s. Closes #2393 + LexManos: + Reinitalize statistics information on Mapping changes. + Fixes mismatched stats to item/block ids. And auto registers stats for modded items. + Closes #2454 + +Build 1.9.4-12.17.0.1927-1.9.4: + LexManos: Fire LivingSpawnEvents for MobSpawners. Closes #2079 + LexManos: Fix javadocs for BiomeColor events. Closes #2093 + LexManos: Fixed EntityRegistry.addSpawn adding duplicate spawn entries. Closes #2112 + LexManos: Fix setTileEntity causing the world to remove the new and old tile entities. Closes #2164 + LexManos: Add ServerHangWatchdog to TerminalTransformer whitelist. Closes #2203 + +Build 1.9.4-12.17.0.1926-1.9.4: + LexManos: Set Enchantment table's harvest level. Closes #1349 + LexManos: More uniformly apply Configuration.caseSensitiveCustomCategories. Closes #1353 + LexManos: Close player's inventory to prevent potential dupe issues on some modded items. Closes #1431 + LexManos: Move getArmorModel hook up two lines so that the attributes and animation information is set. Closes #1939 + LexManos: Fire PopulateChunkEvent Pre and Post in ChunkProviderFlat. Closes #2050 + +Build 1.9.4-12.17.0.1925-1.9.4: + LexManos: Fix installer downloading vanilla jar when not needed. And filter some more known libraries. + LexManos: Capture interfaces when scanning classes and preload implementations of ICrashReportDetail. + +Build 1.9.4-12.17.0.1924-1.9.4: + cpw: Strip Optional from the class signature as well, since that's loaded by the TypeToken registry code and might cause a crash. + +Build 1.9-12.16.1.1923: + shadowfacts: Pass the correct position into isSideSolid in BlockRailBase#canPlaceBlockAt (#2919) + +Build 1.9.4-12.17.0.1922-1.9.4: + tterrag1098: Add hook for TE data sync on client (#2893) + +Build 1.9.4-12.17.0.1921-1.9.4: + the.f1repl4ce: Switch new constructor to use floats (#2904) + +Build 1.9.4-12.17.0.1920-1.9.4: + atomicblom: Add Unit Test to detect mismatches between EnumHelper and Enum ctor + LexManos: Fix paramters for addArmorMaterial, addOption and addGameType. + LexManos: Make unit test for EnumHelper more robust and less spammy. Now detects missing accessors as well. + +Build 1.9.4-12.17.0.1918-1.9.4: + LexManos: Add alternative ItemAxe Constructor + +Build 1.9.4-12.17.0.1917-1.9.4: + LexManos: Fix villagers having farmer skin on vanilla servers. Closes #2894 + +Build 1.9.4-12.17.0.1916-1.9.4: + LexManos: Fire playerDestroyItem event's in some cases where we were not. + +Build 1.9.4-12.17.0.1915-1.9.4: + LexManos: Fix Container.compouteStackSize not respecting stack version of maxStackSize. Closes #2756 + +Build 1.9.4-12.17.0.1914-1.9.4: + LexManos: Set max enchantment ID to Short.MAX_VALUE. Closes #2799 + +Build 1.9.4-12.17.0.1913-1.9.4: + LexManos: Tipped arrows are now not affected by infinite enchantment but are still effected by creative mode. + +Build 1.9.4-12.17.0.1912-1.9.4: + tterrag1098: Fix screenshot link paths on Windows (#2886) + LexManos: Take capabilities into account when comparing ItemStack NBT's. Closes #2726 + +Build 1.9.4-12.17.0.1910-1.9.4: + cpw: Tweak build file slightly - allow passing buildNumber using gradle parameters (-PbuildNumber=...) and provide a "resetBuildNumber" function to reset the buildNumber to zero in the ForgeVersion file. Means builds can be reproducible in-IDE. + +Build 1.9.4-12.17.0.1909-1.9.4: + LexManos: Update example mod for new mappings. + +Build 1.9-12.16.1.1907: + bonii-xx: + Fix Bucket rendering and add sound (#2865) + + * Adjust generated quads from texture to take texture size into account #2858 + + * Fix universal bucket not passing through when rightclicking on a non-block + Add proper sound events when picking up modded fluids #2821 + + * Have FluidUtil execute properly on both sides and sync as expected. Add default sounds to interacting with tanks + + * Save sound for filling/emptying in the fluid + +Build 1.9-12.16.1.1906: + LexManos: Lets just simply this. Fixes last commit. + +Build 1.9-12.16.1.1905: + LexManos: Add modder facing API for Villager Career level trades. Ref: #2854 + +Build 1.9-12.16.1.1904: + cpw: Fix up release jsons to clean up extraneous netty references + fry: Extended Animation State Machine format to allow multiple transitions from one state. + +Build 1.8.9-11.15.1.1902-1.8.9: + cpw: Fix up release jsons to clean up extraneous netty references + +Build 1.9-12.16.1.1901: + cpw: Fix up release jsons to clean up extraneous netty references + +Build 1.9-12.16.1.1900: + LexManos: Make LootPool constructor public again. Closes #2847 + +Build 1.9-12.16.1.1899: + LexManos: Fix type in LootEntry json name. And rename LootTable.finalize to freeze to avoid overriding Object function. Closes #2846 and #2847 + +Build 1.9-12.16.1.1898: + fry: Fixed joint animation application correctly. + +Build 1.9-12.16.1.1897: + shadowfacts: + Add ScreenshotEvent (#2828) + + Add ScreenshotEvent + +Build 1.9-12.16.1.1896: + LexManos: + New LootTable interaction system and event. + Modders can now modify and reference internal elements of a loot table by name. + Editing can ONLY be done in the event and any external editing will cause a exception to be thrown. + See this gist for more information: https://gist.github.com/LexManos/77c983d67b9ad27010428478b66d50fd + +Build 1.9-12.16.1.1895: + fry: Fixed joint hierarchy not being used fully in the animation system. + +Build 1.9-12.16.1.1894: + LexManos: Fixed issue in ChunkIO that would potentially cause NPEs on chunks. Closes #2837 + +Build 1.9-12.16.1.1893: + LexManos: Fixed map extension recipie. + +Build 1.9-12.16.1.1892: + mezz: Fix the rand given to PopulateChunkEvent (#2834) + +Build 1.9-12.16.1.1891: + LexManos: Fix areas where dimension types are used as dimension IDs. + +Build 1.9-12.16.1.1889: + LexManos: Add NPE protection to in-hand item rendering. + +Build 1.9-12.16.1.1888: + mattmess1221: + Fix url regex (#2815) + + Fixed url detection in chat messages. Stops false positives such as `um.....no` + +Build 1.9-12.16.1.1887: + LexManos: Slightly better practice because wait can be randomly interupted. + LexManos: Bump version for new RB. + +Build 1.9-12.16.0.1886: + cpw: Don't ignore rejects + cpw: Add in mcp named patches and use them. Initial 1.9 setup. + cpw: Add rejects with mcp names for application to main codebase. Let's roll? + cpw: First few patches applied + LexManos: net.minecraft.block.* patches + LexManos: util, tileentity, stats, realms. Potions got an overhaul and out registry will need to be adapted. + LexManos: Some import renames and compile error fixes, killed ~800. + fry: Updated various block model patches. + fry: A bunch of rendering rejects updated. + fry: RendererLivingEntity, RenderEntityItem, RenderItem, RenderManager, LayerArmorBase; Item.getModel + ISmartItemModel are now inside ItemOverrideList; fix class rename in TESR patch. + fry: EntityRenderer, most of RenderGlobal, minor fix to LayerBipedArmor. + fry: tabs -> spaces. + fry: FontRenderer + fry: Fixed most errors in the model stuff, except for ModelLoader and b3d and obj getQuads/handleBlockState methods. + LexManos: Some entity patches. + CovertJaguar: Fix broken patches for EntityMinecart + LexManos: Items done, <1000 errors whoot! + LexManos: Small renames before bed. + fry: GuiCreateWorld, GuiSlot, GuiUtilRenderComponents, GuiContainerCreative + fry: GuiOverlayDebug, ItemModelMesher, RenderManager, Stitcher fixed + minor fixes in forge gui classes. + fry: removed Item.getModel, functionality is now achieveable via ItemOverrides. + fry: Updated raw types in ExtendedBlockState, fixed some ATs, updated some things that needed updating in model classes. + fry: Chunk + cpw: + DedicatedServer + IntegratedServer + Adubbz: + Updated the biome dictionary + + Squashed commits: + + [4064de6] Updated the biome dictionary + LexManos: Enchantments, some world, Biomes, bucks, world/gen/features. + Adubbz: Updated BiomeManager to 1.9 + LexManos: Delete reject files i missed + LexManos: More work on misc things. + LexManos: World Patches work. + fry: Fix various errors in text mods. + fry: Fixed B3D loader, fixed some obvious errors in ModelLoader. + cpw: + LanguageManager + NetHandlerPlayClient + fry: ModelBox, PositionTextureVertex, TexturedQuad. Does anyone really use those on the server? + cpw: + ServerPinger + PlayerList + cpw: + WorldServer: NOTE - ChestGenHooks has NOT been updated in accordance with plans for removing it + GuiStats + Fixup deletion of egg handling in entity registry? + fry: Removed generics from IModel subinterfaces, add ed ModelProcessingHelper instead. + fry: Model UV lock handling from the state to the model, fixed most compile errors in ModelLoader, disabled it (and ModelBakeEvent) until it's functional, fixed some errors in ForgeHooksClient. + fry: Made OBJModel.java compile. + LexManos: Fixed errrors in Fluids package. TODO: Make BlockLiquid implement IFluidBlock and REMOVE FluidContainerRegsitry. Everything *should* be able to use IFluidContainer directly. + LexManos: Fishing is now a loot table no more need for FishingHooks. {LootTables still need to be evaludated if they need extra hooks, but thats later} Goodbye 44 compile errors! + LexManos: Temporary hack to fix MCP mappings for param names we are using that is causing compile errors. + LexManos: NetworkDispatcher/FMLProxyPacket 41 more errors cleaned. + cpw: Some fixups for FML, and use the FML registry for potions + cpw: Some more FML related fixups + cpw: Fix up import in Potion + cpw: Some client handler cleanup + cpw: Remove two patches that aren't needed anymore + cpw: Command fixups + cpw: Few more compile fixups + LexManos: More patch work, client patches. + LexManos: + 10 more rejects down. + 98 errors 6 rejects left. + fry: World + fry: Most of ItemInWorldManager reject, various small error fixes. + cpw: Some more forge code fixes + cpw: + Another compilation fix + More patch tweaks for compilation errors. onItemUseTick takes an entity now, cos skellies use stuff too + cpw: More fixups, removing chestgenstuff aggressively. Use loot tables. Any missing ones WILL be added by Mojang. + cpw: Remove more chestgenhooks stuff. Clean up some more ATs + cpw: Potion cleanup. Moar fixes! + cpw: Finish world, chunkloading should work again? + cpw: Another AT, for the player + cpw: Fix up PlayerSP for the AT + LexManos: Interaction hooks need to be re-added but compiles {Doesn't run} + LexManos: + Added bypass functions to Defaulted registry, DO NOT USE THIS MODDERS FORGE INTERNAL ONLY. + And some other fixups for running. + LexManos: Bump version info. Rather important. + LexManos: Fixed position being shifted before being sent to shouldSideBeRendered. + fry: Fix perspective transformations for left-handed items, disable ModelAnimationDebug until model loading is fixed. + cpw: Fix race condition between server ticks and the netlogin code handshaking for FML + cpw: Move the patch into the fml override handler, for less patch + fry: Fix items rendering too low in first person. + fry: Fixed (hopefully) perspective transformations for custom models too. + cpw: Capture Biome Registry within FML + fry: Fixed emply hand not rendering in first person. + fry: Fixed incorrect rendering state caused by transparent rendering pass for entities. + fry: Fixed armor rendering + cpw: Switch to srg patches + fry: Updated to latest mappings. Exc is broken, some anonymous classes didn't map to srg names. + fry: Fixed some errors in forge.exc + fry: First version of updated of ModelLoader, mostly works. + fry: Big model loader refactoring: simplified a lot of things, broke some error reporting. Still generally works. + LexManos: Update patches for fixed inner class suffeling in srg files. + LexManos: We are based on 1.9 not 1.8.9 :D + LexManos: Delete mcp patches. + LexManos: PlayerManager updated. + LexManos: Fix digging blocks in survival. + LexManos: Fixed breaking of tall grass. It now uses fortune. Expanded grass seed hooks to allow Fortune. Potentially removing in future in favor of LootTables. + vazkii: + 1.9: Fixed registering armor materials through EnumHelper exploding + + 1.9 ArmorMaterial now requires a SoundEvent for the equip sound. + fry: Fixed model error reporting, fixed model errors in test mods that shouldn't happen, fixed custom texture loading, made more things private/final. + fry: Fixed incorrect rotations for items in the left hand. Closes #2548. + fry: Fixed incorrect culling of mod TESRs. + fry: Fixed EffectRenderer patch, closes #2547. + fry: Removed imports in patches. + fry: Fixed progress reporting for model loading. + fry: Javadocs, small cleanup. + LexManos: Remove our entity position fixer. Vanilla fixed the bug in 1.9. + LexManos: Fix vanilla bug where bows consumed tipped arrows in creative. + LexManos: Fixed onUseStop being called twice {Bows firing twice} + LexManos: Fixed Sand not falling. + LexManos: Fixed NPE when sneak using a item. + Adubbz: BlockColors and ItemColors no longer assume non-Vanilla id constancy. Added a getter for ItemColors. + fry: Fixed MultiLayerModel not getting correct submodels; Unified the gui lighting of normal and custom models - diffuse lighting is now done in the pipeline, no need for IColoredBakedQuad anymore. + fry: Fixed quads that don't need diffuse lighting getting it anyway. + LexManos: Fix AT lines. + fry: Fixed invalid index calculation in BakedQuadRetextured. + fry: Fixed Block.doesSideBlockRendering, closes #2564. + fry: Fixed outline shader rendering, closes #2560. + fry: Fixed sprite not being passed to the quad builder for custom models. + cordonfreeman: Fix for patch targetting the wrong field to change for failed pathfinding penalty + iTitus: + Fix the EntityPlayer patch + + In 1.8.9 the call goes to getDisplayNameString() so that any changes from the PlayerEvent.NameFormat event are being take into account. + In this patch the call goes to func_70005_c_() which is the getter for the GameProfile name. + I changed it back. + + Sorry if you do not want to accept it because it is a one-liner. + vincent.lee: Fix #2555 + diesieben07: Fix broken patch in EntityPlayer.updateRidden + blay09: + Fix KeyInputEvent only being fired if Keyboard.getEventKeyState() is false. + + It used to be called for both key-up and key-down states prior to 1.9, so I assume Vanilla's changes to F3 behavior broke the patch. + matthewprenger: Pass exceptions thrown in mod event buses back to FML to handle apropriately + vincent.lee: Fix double dropping of items. Closes #2549 + cpw: Ignore classes directory + cpw: Fix bucket test + diesieben07: Fix EntityList.func_188429_b not supporting mod-entities, fixes spawn eggs, fixes #2581 + CovertJaguar: Fix #2601 Minecart infinite acceleration + gigaherz: Fix a condition that caused the enchantment table to roll invalid enchantments. + fry: Switched animation system to capabilities, added animated item example, fixed state passing in MultiModel. + fry: Separated model classes to client and common packages. + fry: instance -> INSTANCE + vincent.lee: Expose IItemHandler on vanilla entities + fry: Implemented slightly more generic version of UVLock, re-enabled it for json models. Closes #2607. + fry: Removed blockCenterToCorner from TRSRTransformation constructor. Closes #2461. + fry: Ignore blocks/items with null registry name during model loading. Fixes NPE during resource reloading in worlds with removed blocks/items. + fry: Made VertexBuffer.sortVertexData cleanup pointers after it's done. Closes #2528. + fry: Added default left hand transforms for forge transform strings. Closes #2615. + elpatricimo: + Allow players sized smaller than 1 block to walk into small spaces + + Same as #2605 but for 1.9 branch + LexManos: Rework DimensionManager for new DimensionType enum, replaces the old provider registry. Also fixed save folder issues with dimensions. Closes #2570 + LexManos: Fix Chests not opening correctly with semi-solid blocks on top. + LexManos: Fixed Item.shouldCauseReequipAnimation hook. + LexManos: Add ShieldDecoration and Tipped arrows to recipe sorter. Closes #2613 + LexManos: Exclude jna from termal tansformer. + vincent.lee: Update according to suggestions + LexManos: Fix pushing players inside blocks. + LexManos: Make RenderLivingBase.add/remvoeLayer, Closes #2573 + LexManos: Added support for custom dyes with Banners. Closes #2596 + LexManos: Allow finite fluids to be drained correctly + LexManos: Make OreDictionary.initVanillaEntries() private so that dumb modders will stop calling it. + vincent.lee: Actually fix dupe drop + LexManos: Fix swap animations for sure this time. + fry: Added Capability.cast, to allow avoiding unchecked casts in ICapabilityProvider.getCapability + LexManos: Update FML Entity Spawn packet for 1.9's location change. Closes #2567 + LexManos: Fixed custom entities unique ids. + LexManos: Fix typo causing biomes to be generated in wrong chunks. Closes #2632 + fry: Show meaningful error if ModelLoaderRegistry is used before the missing model is initialized. + fry: Register the animation Capability. No idea how it worked before at some point. + iTitus: + Add flashing update notification icon made by @gigaherz, closes #2582 + + It is added to the "Mods" button in the main menu and to out-of-date mods in the mod list (there it replaces the "U"). + Also fixes a little typo. + diesieben07: Fix BlockCrops.getDrops not respecting new age methods (for beetroots) + diesieben07: Fix PopulateChunkEvent.Post not firing + LexManos: + New Builder class in BlockStateContainer. Makes building containers with both listed and unlisted properties cleaner. + Make all methods of BiomeGenBase$BiomeProperties public so that modders can use that class outside subclasses. + fry: Cleanup: removed IEEP, removed redundant casts, fixed imports, fixed typos. + fry: Fixed isSideSolid causing infinite loops due to the call to getActualState. + fry: Removed LanguageRegistry and CollectionWrapperFactory. + fry: Removed RenderWorldEvent, encapsulated all public event fields. + fry: Made some more public fields either private or final. + fry: Disabled erroring block, fixed DynBucketTest.TestItem model. + mezz: + Make tooltips layout in the right direction, wrap if there is no room + + Same as #2649, but for Minecraft 1.9 + fry: Fixed diffuse lighting not being applied if forge lighting pipeline is disabled, closes #2651 + fry: Enabled diffuse lighting by default in UnpackedBakedQuad.Builder. + fry: Fixed crosshair always being white, closes #2653. + kashike: + Replace Forge's `BlockPos#getImmutable` method with the included `BlockPos#toImmutable` (func_185334_h), while keeping the override in PooledMutableBlockPos to prevent mutable leaks. + Also prevent a mutable blockpos leak in World#setTileEntity + gigaherz: Add wrapper methods for IStorage#readNBT/writeNBT. + mezz: Fix Block.getPickBlock returning an ItemStack with a null Item + fry: Prevent missing model from loading multiple times. + fry: Fixed StackOverflow caused by the previous commit, closes #2669. + LexManos: Fix EntityPlayer still running old armor logic. Closes #2670 + LexManos: Fix shrubs not generating correctly. Closes #2663 + LexManos: Fix bows not animating properly when picking up ammo while using. Closes #2672 + LexManos: Make NoteBlockEvent raw constructor protected to allow subclasses. Closes #2153 + LexManos: Fix landing particles not showing up. Cloes #2661 + shadowfacts: Forward ItemBlock#addInformation to Block#addInformation + tterrag1098: Add state param to canRenderInLayer + hea3venmc: Fix remapped blocks being overriden with dummy air blocks. Closes #2491 + cpw: + Squashed commit of the following: + + commit b3b290aec9d3010a134859da6001ea28a96c2fdc + Merge: c6ce6a0 d803f7d + Author: cpw + Date: Fri Mar 25 13:28:04 2016 -0400 + + Merge branch 'RegistryRework' of https://github.com/LexManos/MinecraftForge into LexManos-RegistryRework + + Implement proper registry slaves. Should help with rollback related issues. + + Missing patch + + commit d803f7db76f65db9d27302c9804a643bc853dc22 + Author: LexManos + Date: Tue Mar 22 03:36:14 2016 -0700 + + Update VillagerRegistry and use it. Should in theory make custom villagers work now. Using string version instead of int id for networking. + + commit eb5e5b4b42fdca26d2a104e4dc1e6a3ea3051a7b + Author: LexManos + Date: Tue Mar 22 02:14:16 2016 -0700 + + More cleanup. + + commit edbc56b2ff314629d0e402709f3cf29fc79c4a3d + Author: LexManos + Date: Tue Mar 22 02:05:23 2016 -0700 + + More cleanups, removed deprecated UniqueIdentifier {ResourceLocation now} + + commit e2df8d1be3c97601508f83dc97b0e8853fa1e271 + Author: LexManos + Date: Tue Mar 22 01:29:19 2016 -0700 + + Stupid generics.... + + commit 46d57dc4677fa5ff3923e64eaccfb33d7e5aad8d + Author: LexManos + Date: Tue Mar 22 01:00:25 2016 -0700 + + Some registry tweaking to provde a non-complicated API modders can use. + cpw: Reconcile Block.patch + cpw: + Add registries for soundevents, enchantments and potiontypes + MinecraftForge-2576 [1.9] SoundEvents (and Enchantments and PotionTypes) need a FML registry + cpw: MinecraftForge-2683 InvocationTargetException for Forge 1820 for 1.9 + cpw: MinecraftForge-2684 [1.9] New Registry ignores keys + fry: Improved UV offset hackery - should fix most visible custom model seams. + cpw: Support ResLocations for IMC + cpw: Capture a vanilla freeze - will be used when FML connects to vanilla servers, soon + LexManos: Fixed compile error in registry code with Eclipse. + mezz: + Add key binding modifiers and contexts. + + Same as #2674, but for Minecraft 1.9 + Adubbz: Fixed mismatch registry names and mod ids + LexManos: Properly deprecate and link the replacement methods in GameRegistry. Add helper method for registering a block with default ItemBlock because people keep complaining -.- + fry: Changed generic signature of GameRegistry.register methods to work around the type inference bug; updated all example mods to the new block/item registration method. + fry: + Revert "Add key binding modifiers and contexts.", until it's fixed. + + This reverts commit 34c3af7e853d578c8e17e1f0cdf886251fad74ae. + mezz: Re-Add key binding modifiers and contexts. + mezz: Fix inability to attack while holding modifier keys + mcjty1: + Added DimensionManager.createProviderFor() to WorldClient constructor similar to what is done + in WorldServer to make sure the correct provider is created client-side too. + AEnterprise: onBlockHarvested is no longer called twice + fry: Added the ability to change the printed model error count; Added printing of actual exceptions causing missing variants related to blockstate loading, closes #2689. + fry: Made both exceptions occuring during item model loading print in the log; closes #2696. + LexManos: Fix issue caused by setting spawnRadius to 0. Closes #2624 + LexManos: Fixed EntityJoinWorldEvent not being fired for some entitites on Server Worlds. Closes #2685 + LexManos: Fix improper logic in ItemHandlerHelper.giveItemToPlayer causing some items to not be added. Closes #2705 + mezz: + Add modifier support to vanilla keybindings. + + Add Orange conflict color for modifier/key conflicts + (like Ctrl and Ctrl-Z conflicting) + Related to #2692 + vincent.lee: Player Interact Event + Zaggy1024: Fixed using PlaySoundEvent to replace a sound with a PositionedSound causing an NPE due to the Sound field not being set by a call to ISound.createAccessor(SoundHandler). + fry: Fixed mod languages not being loaded on the server. + fry: Fixed some test mods not being marked as client-only. + iTitus: Fix dynbucket item transformation. + fry: Fixed zip being closed too early in the server language loading. + fry: Fixed forge fluid having a collision box. + kat.swales: Corrected CapabilityItemHandler.readNBT ignoring anything in slot 0 in 1.9 + mezz: Fix #2717 Pick block hotkey not working in inventories + mezz: Fix some plain keybinds not working when a modifier is active + LexManos: Enhance some error logging related to OBJLoader issues, and RegistryEntries. + LexManos: + Cleanup OBJLoader parse function and fix issues related to JVM differences. + Also fix support for sopme of the spec that was partially respected. + LexManos: Fixed NPE in dedicated server languages. And fixed logger for main FML event bus. + fry: Made forge fluids use smooth lighting. + kashike: + Remove @SideOnly(Side.CLIENT) from BossInfo/BossInfoServer methods + + These methods can also be used by the server (see BossInfoServer, it sends packets to the client but the methods are @SideOnly(Side.CLIENT)) + LexManos: Fix potential desync between Forge's Villager profession and vanilla's int based system. + LexManos: Fix being kicked from server when climbing ladders. + matti.j.ruohonen: Fix ForgeChunkManager world unloading check (#2736) + mezz: + Add cancelable event for Potions shifting the gui position (#2667) + + Add cancelable event for Potions shifting the gui position + matthewprenger: Don't use import static with net.minecraft classes. Using the latest MCP snapshots this causes an import conflict. (#2742) + CrafterKina: make WorldSavedData implement NBTSerializable (#2745) + mezz: Fix log spam from invalid key modifiers (#2746) + LexManos: Expose a central place to access all of Vanilla and Forge's registries using the new registry API. + iTitus: + Fix forge:default-block transformation. (#2760) + + The first-person left-hand rotation was a little bit of. + vincent.lee: + Boss bar render event (#2701) + + Allow control over increment height + LexManos: Fix NPE on shield break. Closes #2786 + cpw: Add the new license text. Not yet applicable to forge. + cpw: + Update LICENSE-new.txt + + Clarification on infectivity + cpw: + Update LICENSE-new.txt + + Words + cpw: + Update LICENSE-new.txt + + Better words + mezz: Close #2780 add CMD localization for Mac key bindings (#2792) + bonii-xx: + Fix SidedInvWrapper accessing wrong slots for setStackInSlot. (#2797) + + Fix DoubleChestItemHandler not implementing IItemHandlerModifiable + LexManos: Fix sluggish scrolling on GuiScrollList's and fix small rendering issue with scroll bar on certian screen sizes. + bloodmc: + Only run block physics for TileEntities while capturing block placement. (#2803) + + Currently, all blocks placed by players that are not TE's run physics + twice. Blocks that contain a TileEntity are not affected due to a check in + 'ForgeHooks.onPlaceItemIntoWorld'. + + In order to fix the problem, 'Chunk.setBlockState' will now verify if + blocks are being captured before running onBlockAdded and if so, only run + physics if the block has a tileentity. This check also prevents blocks such + as TNT's from running its physics (explosion) when event is cancelled. + williewillus: Add Potion.renderHUDEffect (#2798) + bloodmc: + Call markDirty when restoring blocks with TileEntities. (#2809) + + This change makes sure the updated tileentity is saved properly within the + chunk. + AlexIIL: Fix TextureMap failing when registering a sprite's resource location twice (#2785) + fry: Fixed custom fluid sufraces not rendering from the bottom. Closes #2800. + fry: Make ChunkRenderDispatcher.countRenderBuilders configurable. Closes #2775. + fry: Fixed cooldown overlay sometimes rendering opaque. Closes #2772. + fry: Fixed villager profession not being set correctly on the client, and fixed custom village texture rendering. Closes #2766. + LexManos: Fix withers breaking bedrock. Closes #2813 + LexManos: + Add a java version detection and nag system for users on Java 7 or below. + Added detection of mods that rely on Java 8 and a graceful error screen. + The nag screen will be shown once a day. It can be disabled by editing the forge.cfg. + However it is HIGHLY recomended that user update to Java 8. + LexManos: Remove usage of AsynchronousExecutor library in favor or a simpler implementation. + cpw: Merge in a fix from 1.8.9 for rails + +Build 1.9-12.16.0.1885-1.9: + LexManos: Remove usage of AsynchronousExecutor library in favor or a simpler implementation. + +Build 1.9-12.16.0.1884-1.9: + LexManos: + Add a java version detection and nag system for users on Java 7 or below. + Added detection of mods that rely on Java 8 and a graceful error screen. + The nag screen will be shown once a day. It can be disabled by editing the forge.cfg. + However it is HIGHLY recomended that user update to Java 8. + +Build 1.9-12.16.0.1883-1.9: + LexManos: Fix withers breaking bedrock. Closes #2813 + +Build 1.9-12.16.0.1882-1.9: + fry: Fixed villager profession not being set correctly on the client, and fixed custom village texture rendering. Closes #2766. + +Build 1.9-12.16.0.1881-1.9: + fry: Fixed cooldown overlay sometimes rendering opaque. Closes #2772. + +Build 1.9-12.16.0.1880-1.9: + fry: Make ChunkRenderDispatcher.countRenderBuilders configurable. Closes #2775. + +Build 1.9-12.16.0.1879-1.9: + fry: Fixed custom fluid sufraces not rendering from the bottom. Closes #2800. + +Build 1.9-12.16.0.1878-1.9: + AlexIIL: Fix TextureMap failing when registering a sprite's resource location twice (#2785) + +Build 1.9-12.16.0.1877-1.9: + bloodmc: + Call markDirty when restoring blocks with TileEntities. (#2809) + + This change makes sure the updated tileentity is saved properly within the + chunk. + +Build 1.8.9-11.15.1.1875: + bloodmc: + Call markDirty when restoring blocks with TileEntities. (#2807) + + This change makes sure the updated tileentity is saved properly within the + chunk. + +Build 1.9-12.16.0.1874-1.9: + williewillus: Add Potion.renderHUDEffect (#2798) + +Build 1.8.9-11.15.1.1873: + bloodmc: Fix wrong blockstate being passed to notifyNeighborsRespectDebug. (#2806) + +Build 1.8.9-11.15.1.1872: + bloodmc: + Only run block physics for TileEntities while capturing block placement. (#2805) + + Currently, all blocks placed by players that are not TE's run physics + twice. Blocks that contain a TileEntity are not affected due to a check in + 'ForgeHooks.onPlaceItemIntoWorld'. + + In order to fix the problem, 'Chunk.setBlockState' will now verify if + blocks are being captured before running onBlockAdded and if so, only run + physics if the block has a tileentity. This check also prevents blocks + such as TNT's from running its physics (explosion) when placement event is + cancelled. + +Build 1.9-12.16.0.1871-1.9: + bloodmc: + Only run block physics for TileEntities while capturing block placement. (#2803) + + Currently, all blocks placed by players that are not TE's run physics + twice. Blocks that contain a TileEntity are not affected due to a check in + 'ForgeHooks.onPlaceItemIntoWorld'. + + In order to fix the problem, 'Chunk.setBlockState' will now verify if + blocks are being captured before running onBlockAdded and if so, only run + physics if the block has a tileentity. This check also prevents blocks such + as TNT's from running its physics (explosion) when event is cancelled. + +Build 1.9-12.16.0.1870-1.9: + LexManos: Fix sluggish scrolling on GuiScrollList's and fix small rendering issue with scroll bar on certian screen sizes. + +Build 1.9-12.16.0.1869-1.9: + bonii-xx: + Fix SidedInvWrapper accessing wrong slots for setStackInSlot. (#2797) + + Fix DoubleChestItemHandler not implementing IItemHandlerModifiable + +Build 1.9-12.16.0.1868-1.9: + cpw: + Update LICENSE-new.txt + + Clarification on infectivity + cpw: + Update LICENSE-new.txt + + Words + cpw: + Update LICENSE-new.txt + + Better words + mezz: Close #2780 add CMD localization for Mac key bindings (#2792) + +Build 1.9-12.16.0.1867-1.9: + cpw: Add the new license text. Not yet applicable to forge. + +Build 1.9-12.16.0.1866-1.9: + LexManos: Fix NPE on shield break. Closes #2786 + +Build 1.9-12.16.0.1865-1.9: + vincent.lee: + Boss bar render event (#2701) + + Allow control over increment height + +Build 1.9-12.16.0.1864-1.9: + izooDee5: + Fix forge:default-block transformation. (#2760) + + The first-person left-hand rotation was a little bit of. + +Build 1.9-12.16.0.1863-1.9: + LexManos: Expose a central place to access all of Vanilla and Forge's registries using the new registry API. + +Build 1.9-12.16.0.1862-1.9: + LexManos: Fix log spam from invalid key modifiers (#2746) + +Build 1.9-12.16.0.1861-1.9: + LexManos: make WorldSavedData implement NBTSerializable (#2745) + +Build 1.9-12.16.0.1860-1.9: + LexManos: Don't use import static with net.minecraft classes. Using the latest MCP snapshots this causes an import conflict. (#2742) + +Build 1.9-12.16.0.1859-1.9: + LexManos: + Add cancelable event for Potions shifting the gui position (#2667) + + Add cancelable event for Potions shifting the gui position + +Build 1.9-12.16.0.1858-1.9: + LexManos: Fix ForgeChunkManager world unloading check (#2736) + +Build 1.9-12.16.0.1857-1.9: + LexManos: Fix being kicked from server when climbing ladders. + +Build 1.9-12.16.0.1856-1.9: + LexManos: Fix potential desync between Forge's Villager profession and vanilla's int based system. + +Build 1.8.9-11.15.1.1855: + LexManos: + Update ore dictionary (#2721) + + Updated the Ore Dictionary with more vanilla items: + -Prismarine Shard ( "gemPrismarine" ) + -Prismarine Crystals ( "dustPrismarine" ) + -Prismarine Block ( "blockPrismarine" ) + -Prismarine Brick ( "blockPrismarineBrick" ) + -Dark Prismarine ( "blockDarkPrismarine" ) + -Nether Wart ( "cropNetherWart" ) + -Sugarcane ( "sugarcane" ) + -Paper ( "paper" ) + -Ender Pearl ( "enderpearl" ) + -Bone ( "bone) + -Gunpowder ( "gunpowder) + -String ( "string" ) + -Nether Star ( "netherStar" ) + -Leather ( "leather" ) + -Feather ( "feather" ) + -Egg ( "egg" ) + -End Stone ( "endstone" ) + -Torch ( "torch" ) + -Vine ( "vine" ) + -Cactus ( "blockCactus" ) + -Grass ( "grass") + -Obsidian ( "obsidian" ) + -Red Sandstone ( added to "sandstone" ) + -Crafting Table ( "workbench" ) + -Slime Block ( "blockSlime" ) + -Granite ( "stoneGranite" ) + -Polished Granite ( "stoneGranitePolished" ) + -Diorite ( "stoneDiorite" ) + -Polished Diorite ( "stoneDioritePolished" ) + -Andesite ( "stoneAndesite" ) + -Polished Andesite ( "stoneAndesitePolished" ) + +Build 1.9-12.16.0.1854-1.9: + kashike: + Remove @SideOnly(Side.CLIENT) from BossInfo/BossInfoServer methods + + These methods can also be used by the server (see BossInfoServer, it sends packets to the client but the methods are @SideOnly(Side.CLIENT)) + +Build 1.9-12.16.0.1853-1.9: + fry: Made forge fluids use smooth lighting. + +Build 1.9-12.16.0.1852-1.9: + LexManos: Fixed NPE in dedicated server languages. And fixed logger for main FML event bus. + +Build 1.9-12.16.0.1851-1.9: + LexManos: Enhance some error logging related to OBJLoader issues, and RegistryEntries. + LexManos: + Cleanup OBJLoader parse function and fix issues related to JVM differences. + Also fix support for sopme of the spec that was partially respected. + +Build 1.9-12.16.0.1850-1.9: + mezz: Fix some plain keybinds not working when a modifier is active + +Build 1.9-12.16.0.1849-1.9: + mezz: Fix #2717 Pick block hotkey not working in inventories + +Build 1.9-12.16.0.1848-1.9: + kat.swales: Corrected CapabilityItemHandler.readNBT ignoring anything in slot 0 in 1.9 + +Build 1.8.9-11.15.1.1847: + kat.swales: Corrected readNBT ignoring anything in slot 0 + +Build 1.9-12.16.0.1846-1.9: + fry: Fixed zip being closed too early in the server language loading. + fry: Fixed forge fluid having a collision box. + +Build 1.9-12.16.0.1845-1.9: + iTitus: Fix dynbucket item transformation. + +Build 1.9-12.16.0.1844-1.9: + fry: Fixed some test mods not being marked as client-only. + +Build 1.9-12.16.0.1843-1.9: + fry: Fixed mod languages not being loaded on the server. + +Build 1.9-12.16.0.1842-1.9: + Zaggy1024: Fixed using PlaySoundEvent to replace a sound with a PositionedSound causing an NPE due to the Sound field not being set by a call to ISound.createAccessor(SoundHandler). + +Build 1.9-12.16.0.1841-1.9: + mezz: + Add modifier support to vanilla keybindings. + + Add Orange conflict color for modifier/key conflicts + (like Ctrl and Ctrl-Z conflicting) + Related to #2692 + +Build 1.9-12.16.0.1840-1.9: + vincent.lee: Player Interact Event + +Build 1.9-12.16.0.1839-1.9: + AEnterprise: onBlockHarvested is no longer called twice + LexManos: Fixed EntityJoinWorldEvent not being fired for some entitites on Server Worlds. Closes #2685 + LexManos: Fix improper logic in ItemHandlerHelper.giveItemToPlayer causing some items to not be added. Closes #2705 + +Build 1.9-12.16.0.1838-1.9: + LexManos: Fix issue caused by setting spawnRadius to 0. Closes #2624 + +Build 1.9-12.16.0.1837-1.9: + fry: Made both exceptions occuring during item model loading print in the log; closes #2696. + +Build 1.9-12.16.0.1836-1.9: + fry: Added the ability to change the printed model error count; Added printing of actual exceptions causing missing variants related to blockstate loading, closes #2689. + +Build 1.9-12.16.0.1835-1.9: + mcjty1: + Added DimensionManager.createProviderFor() to WorldClient constructor similar to what is done + in WorldServer to make sure the correct provider is created client-side too. + +Build 1.9-12.16.0.1834-1.9: + iTitus: + Add flashing update notification icon made by @gigaherz, closes #2582 + + It is added to the "Mods" button in the main menu and to out-of-date mods in the mod list (there it replaces the "U"). + Also fixes a little typo. + +Build 1.9-12.16.0.1833-1.9: + mezz: Fix inability to attack while holding modifier keys + +Build 1.9-12.16.0.1832-1.9: + mezz: Re-Add key binding modifiers and contexts. + +Build 1.9-12.16.0.1831-1.9: + fry: + Revert "Add key binding modifiers and contexts.", until it's fixed. + + This reverts commit 34c3af7e853d578c8e17e1f0cdf886251fad74ae. + +Build 1.9-12.16.0.1830-1.9: + fry: Changed generic signature of GameRegistry.register methods to work around the type inference bug; updated all example mods to the new block/item registration method. + +Build 1.9-12.16.0.1829-1.9: + Adubbz: Fixed mismatch registry names and mod ids + +Build 1.9-12.16.0.1828-1.9: + mezz: + Add key binding modifiers and contexts. + + Same as #2674, but for Minecraft 1.9 + +Build 1.9-12.16.0.1827-1.9: + LexManos: Properly deprecate and link the replacement methods in GameRegistry. Add helper method for registering a block with default ItemBlock because people keep complaining -.- + +Build 1.9-12.16.0.1826-1.9: + LexManos: Fixed compile error in registry code with Eclipse. + +Build 1.9-12.16.0.1825-1.9: + cpw: Capture a vanilla freeze - will be used when FML connects to vanilla servers, soon + +Build 1.9-12.16.0.1824-1.9: + cpw: Support ResLocations for IMC + +Build 1.9-12.16.0.1823-1.9: + fry: Improved UV offset hackery - should fix most visible custom model seams. + +Build 1.9-12.16.0.1822-1.9: + cpw: MinecraftForge-2684 [1.9] New Registry ignores keys + +Build 1.9-12.16.0.1821-1.9: + cpw: MinecraftForge-2683 InvocationTargetException for Forge 1820 for 1.9 + +Build 1.9-12.16.0.1820-1.9: + cpw: + Add registries for soundevents, enchantments and potiontypes + MinecraftForge-2576 [1.9] SoundEvents (and Enchantments and PotionTypes) need a FML registry + +Build 1.9-12.16.0.1819-1.9: + cpw: Fix remapped blocks being overriden with dummy air blocks. Closes #2491 + cpw: + Squashed commit of the following: + + commit b3b290aec9d3010a134859da6001ea28a96c2fdc + Merge: c6ce6a0 d803f7d + Author: cpw + Date: Fri Mar 25 13:28:04 2016 -0400 + + Merge branch 'RegistryRework' of https://github.com/LexManos/MinecraftForge into LexManos-RegistryRework + + Implement proper registry slaves. Should help with rollback related issues. + + Missing patch + + commit d803f7db76f65db9d27302c9804a643bc853dc22 + Author: LexManos + Date: Tue Mar 22 03:36:14 2016 -0700 + + Update VillagerRegistry and use it. Should in theory make custom villagers work now. Using string version instead of int id for networking. + + commit eb5e5b4b42fdca26d2a104e4dc1e6a3ea3051a7b + Author: LexManos + Date: Tue Mar 22 02:14:16 2016 -0700 + + More cleanup. + + commit edbc56b2ff314629d0e402709f3cf29fc79c4a3d + Author: LexManos + Date: Tue Mar 22 02:05:23 2016 -0700 + + More cleanups, removed deprecated UniqueIdentifier {ResourceLocation now} + + commit e2df8d1be3c97601508f83dc97b0e8853fa1e271 + Author: LexManos + Date: Tue Mar 22 01:29:19 2016 -0700 + + Stupid generics.... + + commit 46d57dc4677fa5ff3923e64eaccfb33d7e5aad8d + Author: LexManos + Date: Tue Mar 22 01:00:25 2016 -0700 + + Some registry tweaking to provde a non-complicated API modders can use. + cpw: Reconcile Block.patch + +Build 1.9-12.16.0.1817-1.9: + kashike: + Replace Forge's `BlockPos#getImmutable` method with the included `BlockPos#toImmutable` (func_185334_h), while keeping the override in PooledMutableBlockPos to prevent mutable leaks. + Also prevent a mutable blockpos leak in World#setTileEntity + gigaherz: Add wrapper methods for IStorage#readNBT/writeNBT. + LexManos: Fix landing particles not showing up. Cloes #2661 + shadowfacts: Forward ItemBlock#addInformation to Block#addInformation + tterrag1098: Add state param to canRenderInLayer + +Build 1.9-12.16.0.1816-1.9: + LexManos: Make NoteBlockEvent raw constructor protected to allow subclasses. Closes #2153 + +Build 1.9-12.16.0.1815-1.9: + LexManos: Fix EntityPlayer still running old armor logic. Closes #2670 + LexManos: Fix shrubs not generating correctly. Closes #2663 + LexManos: Fix bows not animating properly when picking up ammo while using. Closes #2672 + +Build 1.9-12.16.0.1814-1.9: + mezz: Fix Block.getPickBlock returning an ItemStack with a null Item + +Build 1.9-12.16.0.1813-1.9: + fry: Fixed StackOverflow caused by the previous commit, closes #2669. + +Build 1.9-12.16.0.1812-1.9: + fry: Prevent missing model from loading multiple times. + +Build 1.9-12.16.0.1811-1.9: + fry: Enabled diffuse lighting by default in UnpackedBakedQuad.Builder. + fry: Fixed crosshair always being white, closes #2653. + +Build 1.9-12.16.0.1810-1.9: + fry: Fixed diffuse lighting not being applied if forge lighting pipeline is disabled, closes #2651 + +Build 1.9-12.16.0.1809-1.9: + mezz: + Make tooltips layout in the right direction, wrap if there is no room + + Same as #2649, but for Minecraft 1.9 + +Build 1.8.9-11.15.1.1808: + mezz: + Make tooltips layout in the right direction, wrap if there is no room + + Fixes #2255 + +Build 1.9-12.16.0.1807-1.9: + fry: Disabled erroring block, fixed DynBucketTest.TestItem model. + +Build 1.9-12.16.0.1806-1.9: + diesieben07: Fix PopulateChunkEvent.Post not firing + +Build 1.9-12.16.0.1805-1.9: + fry: Cleanup: removed IEEP, removed redundant casts, fixed imports, fixed typos. + fry: Fixed isSideSolid causing infinite loops due to the call to getActualState. + fry: Removed LanguageRegistry and CollectionWrapperFactory. + fry: Removed RenderWorldEvent, encapsulated all public event fields. + fry: Made some more public fields either private or final. + +Build 1.9-12.16.0.1804-1.9: + LexManos: + New Builder class in BlockStateContainer. Makes building containers with both listed and unlisted properties cleaner. + Make all methods of BiomeGenBase$BiomeProperties public so that modders can use that class outside subclasses. + +Build 1.9-12.16.0.1803-1.9: + diesieben07: Fix BlockCrops.getDrops not respecting new age methods (for beetroots) + +Build 1.9-12.16.0.1802-1.9: + fry: Show meaningful error if ModelLoaderRegistry is used before the missing model is initialized. + fry: Register the animation Capability. No idea how it worked before at some point. + +Build 1.9-12.16.0.1801-1.9: + LexManos: Fix typo causing biomes to be generated in wrong chunks. Closes #2632 + +Build 1.9-12.16.0.1800-1.9: + LexManos: Fixed custom entities unique ids. + +Build 1.9-12.16.0.1799-1.9: + LexManos: Update FML Entity Spawn packet for 1.9's location change. Closes #2567 + +Build 1.9-12.16.0.1798-1.9: + vincent.lee: Actually fix dupe drop + +Build 1.9-12.16.0.1797-1.9: + fry: Added Capability.cast, to allow avoiding unchecked casts in ICapabilityProvider.getCapability + +Build 1.9-12.16.0.1796-1.9: + LexManos: Fix swap animations for sure this time. + +Build 1.9-12.16.0.1795-1.9: + LexManos: Allow finite fluids to be drained correctly + LexManos: Make OreDictionary.initVanillaEntries() private so that dumb modders will stop calling it. + +Build 1.8.9-11.15.1.1794: + Choonster.2010: Allow finite fluids to be drained correctly + +Build 1.9-12.16.0.1792-1.9: + matthewprenger: Pass exceptions thrown in mod event buses back to FML to handle apropriately + vincent.lee: Fix double dropping of items. Closes #2549 + diesieben07: Fix EntityList.func_188429_b not supporting mod-entities, fixes spawn eggs, fixes #2581 + +Build 1.8.9-11.15.1.1791: + elpatricimo: + Fix pushing players inside blocks. + + same changes made to the 1.9 branch + +Build 1.9-12.16.0.1790-1.9: + vincent.lee: Expose IItemHandler on vanilla entities + vincent.lee: Update according to suggestions + +Build 1.9-12.16.0.1789-1.9: + LexManos: Make RenderLivingBase.add/remvoeLayer, Closes #2573 + LexManos: Added support for custom dyes with Banners. Closes #2596 + +Build 1.9-12.16.0.1788-1.9: + LexManos: Add ShieldDecoration and Tipped arrows to recipe sorter. Closes #2613 + LexManos: Exclude jna from termal tansformer. + LexManos: Fix pushing players inside blocks. + +Build 1.9-12.16.0.1787-1.9: + LexManos: Fix Chests not opening correctly with semi-solid blocks on top. + LexManos: Fixed Item.shouldCauseReequipAnimation hook. + +Build 1.9-12.16.0.1786-1.9: + elpatricimo: + Allow players sized smaller than 1 block to walk into small spaces + + Same as #2605 but for 1.9 branch + +Build 1.8.9-11.15.1.1785: + elpatricimo: Allow players sized smaller than 1 block to walk into small spaces + +Build 1.9-12.16.0.1784-1.9: + LexManos: Rework DimensionManager for new DimensionType enum, replaces the old provider registry. Also fixed save folder issues with dimensions. Closes #2570 + +Build 1.8.9-11.15.1.1783: + CovertJaguar: Add missing Minecart/Track hook. + +Build 1.9-12.16.0.1782-1.9: + fry: Removed blockCenterToCorner from TRSRTransformation constructor. Closes #2461. + fry: Ignore blocks/items with null registry name during model loading. Fixes NPE during resource reloading in worlds with removed blocks/items. + fry: Made VertexBuffer.sortVertexData cleanup pointers after it's done. Closes #2528. + fry: Added default left hand transforms for forge transform strings. Closes #2615. + +Build 1.9-12.16.0.1781-1.9: + fry: Switched animation system to capabilities, added animated item example, fixed state passing in MultiModel. + fry: Separated model classes to client and common packages. + fry: instance -> INSTANCE + fry: Implemented slightly more generic version of UVLock, re-enabled it for json models. Closes #2607. + +Build 1.9-12.16.0.1780-1.9: + gigaherz: Fix a condition that caused the enchantment table to roll invalid enchantments. + +Build 1.9-12.16.0.1779-1.9: + blay09: + Fix KeyInputEvent only being fired if Keyboard.getEventKeyState() is false. + + It used to be called for both key-up and key-down states prior to 1.9, so I assume Vanilla's changes to F3 behavior broke the patch. + +Build 1.9-12.16.0.1778-1.9: + CovertJaguar: Fix #2601 Minecart infinite acceleration + +Build 1.8.9-11.15.1.1777: + cordonfreeman: Fix for patch targetting the wrong field to change for failed pathfinding penalty + +Build 1.9-12.16.0.1776-1.9: + diesieben07: Fix broken patch in EntityPlayer.updateRidden + +Build 1.9-12.16.0.1775-1.9: + iTitus: + Fix the EntityPlayer patch + + In 1.8.9 the call goes to getDisplayNameString() so that any changes from the PlayerEvent.NameFormat event are being take into account. + In this patch the call goes to func_70005_c_() which is the getter for the GameProfile name. + I changed it back. + + Sorry if you do not want to accept it because it is a one-liner. + vincent.lee: Fix #2555 + +Build 1.9-12.16.0.1774-1.9: + cordonfreeman: Fix for patch targetting the wrong field to change for failed pathfinding penalty + +Build 1.9-12.16.0.1773-1.9: + cpw: Fix bucket test + +Build 1.9-12.16.0.1772-1.9: + bernhard.bonigl: + Fix CombinedInvWrapper accessing incorrect slots + Closes #2533 + +Build 1.9-12.16.0.1771-1.9: + cpw: Ignore classes directory + +Build 1.9-12.16.0.1770-1.9: + fry: Fixed sprite not being passed to the quad builder for custom models. + +Build 1.9-12.16.0.1769-1.9: + fry: Fixed outline shader rendering, closes #2560. + +Build 1.9-12.16.0.1768-1.9: + fry: Fixed Block.doesSideBlockRendering, closes #2564. + +Build 1.9-12.16.0.1767-1.9: + fry: Fixed invalid index calculation in BakedQuadRetextured. + +Build 1.8.9-11.15.1.1765: + bernhard.bonigl: + Fix CombinedInvWrapper accessing incorrect slots + Closes #2533 + +Build 1.8.9-11.15.1.1764: + abab9579: + Added EntityViewRenderEvent#FOVModifier + + Enables mods to modify raw FOV directly. + Avoids modifier limit from FOVUpdateEvent. + Aware of blocks which are needed for fov change related with materials. + +Build 1.8.9-11.15.1.1763: + mnmiller1: Added local click position to PlayerInteractEvent. + +Build 1.8.9-11.15.1.1762: + LexManos: + Fixed dummy blocks not being removed from registry on dedicated server correctly. Closes #2487 + Also added some more debug logs! + +Build 1.8.9-11.15.1.1761: + bernhard.bonigl: + Fix WordServer.canCreatureTypeSpawnHere not using Forge Events. + Followup of #2496 + +Build 1.8.9-11.15.1.1760: + agent-lego: + Increased actual maximum fuel burn time + + Increased actual maximum fuel burn time from 32767 to 2147483647. + Backwards compatible. + +Build 1.8.9-11.15.1.1759: + bernhard.bonigl: Fix WorldEvent.PotentialSpawns Event passing the list instance of the ChunkProvider to users + +Build 1.8.9-11.15.1.1758: + fry: ModelLoader: added verbose error logging showing items/blockstates associated with model locations; removed redundant cast leftover from ungenerified code; fixed up a couple of warnings. + +Build 1.8.9-11.15.1.1757: + cpw: Fix race condition when using indexed codec in a LAN server. Fixes spurious crash when LAN server is running. + +Build 1.8.9-11.15.1.1756: + cpw: Log when it's likely the indexedcodec is gonna fail, because of LAN server derps likely. Try and give some context for debuggers. + +Build 1.8.9-11.15.1.1755: + bernhard.bonigl: Fix universal bucket logging an exception + +Build 1.8.9-11.15.1.1754: + AlgorithmX2: Preserve ItemStack Caps when picking Items up. + AlgorithmX2: Test getLightOpacity(world,pos) in World as well as the chunk. + +Build 1.8.9-11.15.1.1752: + AlgorithmX2: In material Liquid Hooks. + +Build 1.8.9-11.15.1.1751: + diesieben07: Fix Potion.shouldRenderInvText + +Build 1.8.9-11.15.1.1750: + AlgorithmX2: + Fix bug with getLightOpacity based on TileEntities, + Capture getLightOpacity prior to removing the tile entity. + +Build 1.8.9-11.15.1.1749: + LexManos: Fix loading of world ID maps for worlds without dummy data. Closes #2477 + +Build 1.8.9-11.15.1.1748: + bernhard.bonigl: Fix customized bucket names not getting translated correctly + +Build 1.8.9-11.15.1.1747: + fry: Added default levels for fluid extended properties in fluid model. + +Build 1.8.9-11.15.1.1746: + bernhard.bonigl: Add universal bucket + +Build 1.8.9-11.15.1.1745: + LexManos: Fix chat formatting not surviving line wraps in chat messages. + +Build 1.8.9-11.15.1.1744: + diesieben07: Fix the entity shader hook + +Build 1.8.9-11.15.1.1743: + iLexiconn: Added hook to add entity spectator shader + +Build 1.8.9-11.15.1.1742: + ross: Workaround JDK-8087309: Constant folding "static final boolean" in boolean expressions is incomplete + +Build 1.8.9-11.15.1.1741: + LexManos: Protect BlockSnapshot and Chunk.getTileEntity from mutible BlockPos instances. Closes #2438 + +Build 1.8.9-11.15.1.1740: + LexManos: Fixed invalid parameters being sent to LivingSpawnEvent.CheckSpawn. Closes #2453 + LexManos: Fixed Entity.shouldRiderSit modifying game logic and not just rendering. Closes #2402 + LexManos: Fixed Block/Item.setRegistryName printing a incorrect warning when used. Closes #2398 + LexManos: Fixed vanilla bug where network clients would see incorrect break progress on blocks. Closes #2462 + LexManos: Fixed implementation of RenderEntityItem.shouldSpreadItems. Closes #2448 + LexManos: Fixed invalid item being passed to gatherCapabilities when itemstacks changed item. Closes #2445 + LexManos: Add missing classes to CrashReport preloading. Closes #2421 + LexManos: Fixed Block.onNeighborChange not being called for all axises. Closes #2428 + +Build 1.8.9-11.15.1.1739: + LexManos: Potential fix for NPE when using an item to open a slotless inventory. Closes #1354 + +Build 1.8.9-11.15.1.1738: + fry: Fix custom model loader reload hook not firing during registration. + +Build 1.8.9-11.15.1.1737: + fry: Fix incorrectly updated patch to ChunkCache. Related to #2451. + +Build 1.8.9-11.15.1.1736: + fry: Fix multiple registrations of reloading hooks in custom model loaders. + +Build 1.8.9-11.15.1.1735: + LexManos: Fix jar signer not applying to universal packed in the installer. + LexManos: Remove Mojang logger spam in EntityItem. + +Build 1.8.9-11.15.1.1734: + fry: Fix TRSRTransformation.toItemTransform. Closes #2425. + +Build 1.8.9-11.15.1.1733: + paul.fulham0: Fix #2321 + +Build 1.8.9-11.15.1.1732: + Mumfrey: Fix ASM SignatureReader choking on lambda signatures generated by JDT + +Build 1.8.9-11.15.1.1731: + fry: Revert binary-incompatible changes in models. + +Build 1.8.9-11.15.1.1730: + fry: Fix droppers not dropping. + +Build 1.8.9-11.15.1.1729: + fry: Added "gui3d" and "smooth_lighting" options to the forge blockstate. Implemented them for vanilla and B3D models. Added generic types to IModel subinterfaces, to simplify chaining. + fry: One day I will learn to include all required classes in the commit. + +Build 1.8.9-11.15.1.1727: + matthewprenger: Log unknown exceptions in commands. Resolves #2433 + +Build 1.8.9-11.15.1.1726: + fry: Fixed incorrect patch to GenLayer, causing large vanilla biomes to not work properly. + +Build 1.8.9-11.15.1.1725: + fry: Provide original armor model in the custom armor model hook. + +Build 1.8.9-11.15.1.1724: + fry: Fixed small bug in item extraction code. + +Build 1.8.9-11.15.1.1723: + fry: Fix NoSuchElementException in ModelBlockAnimation caused by empty event list. + +Build 1.8.9-11.15.1.1722: + LexManos: Bump version in prep for new Recomended Build. + +Build 1.8.9-11.15.0.1721: + LexManos: Loosen TileEntity's default hasCapability implementation. Modders should specifically opt in. + +Build 1.8.9-11.15.0.1720: + vikestepftb: + Add EntityTravelToDimensionEvent + + Resolve Merge Conflict + + Move event call inside conditional + +Build 1.8.9-11.15.0.1719: + fry: Fixed minor race condition between mouse clicks and TESR in the animation example. + +Build 1.8.9-11.15.0.1718: + fry: + Model animation system. + Main things of interest: + * IAnimationStateMachine - state machine for animations; can load + from json. + * AnimationTESR - automatic TESR for animated models. + * AnimationModelBase - same for entities. + * ITimeValue - time-varying value, used to control animation + parameters from code. + + * TESRs can now be batched - look at TESR.renderTileEntityFast + + TE.hasFastRenderer. + * RegionRenderCache is not accessible to TESRs and other client-side + logic - MinecraftForgeClient.getRegionRenderCache. + fry: Sometimes I forget how much trivial things are missing from Java 6. + +Build 1.8.9-11.15.0.1716: + t.tomkins: Fixed: BlockPos passed to isBeaconBase + rwtema: + Add IItemHandler capability + + Add the actual patches that I forgot. + + Add simple implementations of IStorage and the factory methods. + + Add ItemStackHandler. A simple IItemHandler implementaton. + + return nulls, not throw nulls. + + Move the vanilla wrappers to a separate class for now. + + Minor clean ups of VanillaWrapper code. + + Inline static methods. + + Add comments. + + Minor cleanup of code. + + Remove redundant size field and add a validate slot index method. + + Minor formatting issues. + + Break early If stacksize to insert is 0. + + Remove setByte() methods. + + Throw exception if IItemHandler can't be modifyed in NBT loading. + + Replace event handler with patches + + Add capability to mine cart inventory entities. + + Change formatting and registration of capability. + + Make InventoryPlayer implements IItemHandler because why not. Also added a field to allow mods that add additional player inventory space to publicly expose them. + + Reduce patch sizes + + Lazy initialization of the item handler for vanilla tiles. + + Minor formatting changes. + + Create a single vanilla chest item handler that will merge with adjacent chests when detected. Added hooks to reset the cached adjacent value when a block update is detected and when a chunk loads. + + Revert "Make InventoryPlayer implements IItemHandler because why not. Also added a field to allow mods that add additional player inventory space to publicly expose them." + + This reverts commit 306d4a37fd0e8c8a0754411c013b750dfe8e2c87. + + Fix furnace derp + + Replace double chest code with a simpler method. + + Vanilla wrappers implement IItemHandlerModifiable (since they are modifiable) + + Minor code cleanups + + Add an onContentsChanged() and onLoad() callback methods.to the default implementation. + + Add slot as a parameter in the callback method. + + Change IItemHandlerModifiable.setStackInSlot() to void, and added a note about not being intended for cross-mod use. + + Improve ItemStackHandler handling of errored NBT. + + Make the stacks array protected. + + Fix a lot of derps in SlotItemHandler. + + Fix derp in ItemStackHandler + + Clarify comments on IItemHandler + + ItemStackHandler no longer caches the stack array in local variable. + + Clean up the Chests code to make intentions clearer + + Vanilla hoppers have their cooldown activated when an item is inserted. Made this behavior part of an item handler (rather than the insertion code) + + Fix mistake in ItemStackHandler + + More documentation of potential edge cases in getStackInSlot() + + Make limit checking more resiliant. + LexManos: Fix J6 compile error in IItemHandler PR. + +Build 1.8.9-11.15.0.1715: + blay09: Fix items being dropped out of a GuiContainer when dealing with a slot outside of the xSize/ySize boundaries. + +Build 1.8.9-11.15.0.1714: + asiekierka: Fix #2388 + +Build 1.8.9-11.15.0.1713: + LexManos: Fix invalid ServerHangWatchdog patch causing it to not apply. + LexManos: + Restore OreDictionary.getOres(String, boolean) and doeOreNameExist(String) functions that went MIA in git merge issue. + Original Commit: https://github.com/MinecraftForge/MinecraftForge/commit/cd3bbfb02c9fcd4ce4bbf00f460dfdd6a386d107 + +Build 1.8.9-11.15.0.1712: + minecrell: Strip console formatting codes for Vanilla log file + +Build 1.8.9-11.15.0.1711: + iLexiconn: + Fixed makeItemStack ignoring stackSize + + Fixes #2376 + +Build 1.8.9-11.15.0.1710: + fry: Fixed error detection login in ModelLoader, reduced the maximum number of printed stack traces to 5 per domain. + +Build 1.8.9-11.15.0.1709: + LexManos: Fix Deobfusication transformer throwing verification errores in Eclipse development environments. + LexManos: Fix forge logging not working correctly in ForgeDev. + +Build 1.8.9-11.15.0.1708: + fry: Correctly handle error caused by missing/malformed bucket model definition file. + +Build 1.8.9-11.15.0.1707: + LexManos: Call deserialze on capabilities in ItemStack.setItem. Closes #2384 + +Build 1.8.9-11.15.0.1706: + LexManos: Fix ItemStacks not getting the parent capability provider from items. Closes #2383 + +Build 1.8.9-11.15.0.1705: + bartek.bok: Allow sleeping without bed + kashike: Skip hang detection on first run of the server hang watchdog + +Build 1.8.9-11.15.0.1703: + fry: Fixed missing model error reporting, made block and item variants sort before loading, to make the splash screen info more useful. + +Build 1.8.9-11.15.0.1702: + cpw: Fix spruce not growing properly + +Build 1.8.9-11.15.0.1701: + cpw: Fixed problem with LAN connections seeing the original ProxyPacket and consuming the first byte. Closes #2373 + +Build 1.8.9-11.15.0.1700: + cpw: Fix shouldRefresh not to be so over-eager about modded TEs. Should fix Packet21 causing a TE reset. + +Build 1.8.9-11.15.0.1699: + LexManos: Clone FML packet indexes if channel is not open. Fixes potential threading issue. + +Build 1.8.9-11.15.0.1698: + bernhard.bonigl: + Default bucket model is empty, allow bucket model without fluid. + This allows capsules, cans, bottles,... to use the same item model for their item variant. Also it's makes much more sense. + +Build 1.8.9-11.15.0.1697: + mezz: Add GuiScreenEvent.BackgroundDrawnEvent + +Build 1.8.9-11.15.0.1696: + cpw: + Fix problem which meant runtime deobf to MCP names wasn't working. SRG named mods located in the mods dir of a dev + environment will now load normally. + +Build 1.8.9-11.15.0.1695: + LexManos: + New Capability system allowing for more manageable world object features. + + When combined with @Optional this should address all issues of soft dependancy on mods/apis. + This also addresses the issue of dynamic functionality in TileEntities/Entities. + + Current capability providers: TileEntity, Entity, ItemStack + + Also added INBTSerializeable, a generic interface for game objects that can be written to/from NBT tags. + + Vanilla capabilities will be coming soon, mostly on request and review. + So start requesting capabiliteis on vanilla/Forge features. + +Build 1.8.9-11.15.0.1694: + LexManos: Fixed GuiWrongMinecraft having wrong Log file name. Closes #2348 + LexManos: Fix jline server console not showing colored text. Closes #2334 + LexManos: Fixed banners not droping the correct item when harvested using modded mechanics. Closes #2258 + +Build 1.8.9-11.15.0.1693: + LexManos: Remove dead code in JarDiscoverer. Closes #2346 + LexManos: Made WrongMinecraftVersionException and ModSortingException a bit easier to read in log files. Closes: #2345 + +Build 1.8.9-11.15.0.1692: + fry: Catch GROUND item transform type with the forge hook too. + +Build 1.8.9-11.15.0.1691: + fry: Implemented face culling for item models, improves performance slightly. Closes #2326 + +Build 1.8.9-11.15.0.1690: + vincent.lee: + Fix visual effect entity persisting longer than it should + + Fix derp + +Build 1.8.9-11.15.0.1689: + LexManos: Fixed issue with WorldServer.getTileEntities when being called with ranges that overlap chunks oddly. Closes #2350 + +Build 1.8.9-11.15.0.1688: + fry: Made block model loading bar more robust, and fixed the count. + +Build 1.8.9-11.15.0.1687: + fry: Added progress bar for the ModelLoader. + +Build 1.8.9-11.15.0.1686: + fry: Model loader improvements: adding custom data/textures to models that don't need them doesn't cause a error now, since it's common to put those in the defaults section of the blockstate json; you can get IModel associated with the variant now - using ModelLoaderRegistry.getModel; MultiLayerModel should now respect transformations applied to it, and respect part transformations. + fry: Derp. + +Build 1.8.9-11.15.0.1684: + cpw: STFU I know how to code. *shouts* get off my lawn! + +Build 1.8.9-11.15.0.1683: + cpw: Make the parent dir tree for the dep extractor + +Build 1.8.9-11.15.0.1682: + fry: Changed default implementation of Block.getExtendedState. Closes #2309. + +Build 1.8.9-11.15.0.1681: + fry: Fixed perspective transformation handling for fluid models, changed inventory fluid model to use unrotated still texture. + fry: Don't crash on exception during loading of item variants from blockstate jsons. + fry: Fixed implementation if IRetexturableModel in ItemLayerModel. Closes #2244. + fry: Java6 + Eclipse + Gradle strike again. + +Build 1.8.9-11.15.0.1677: + fry: Fixed invalid flow vector caching of still fluids. + +Build 1.8.9-11.15.0.1676: + fry: Fixed random offset not working properly for flatly-lit models. + +Build 1.8.9-11.15.0.1675: + cpw: Explicitly close the file. + +Build 1.8.9-11.15.0.1674: + cpw: + Add a simple ContainedDep mechanic- mods can contain other mods or libs, and can specify them using a manifest tag: "ContainedDeps". + This is a space separated (manifest standard) list of jar files that are to be extracted into the version specific directory. + + There's also a special system property "fml.skipContainedDeps" that will allow for runtime skipping of extraction of contained deps (a comma separated list, based on the file name). + +Build 1.8.9-11.15.0.1673: + LexManos: + Added new get/setRegistryName functions to Item and Block. + And helper functions in GameRegistry to allow for registering using those names automatically. + This is to simplify registration and get rid of the horrible hacks users are doing now with 'unlocalised names'. + +Build 1.8.9-11.15.0.1672: + cpw: Move substitution activation after id loading. This should fix the problem of null for existing object. + +Build 1.8.9-11.15.0.1671: + fry: SidedProxy now has sensible default values - nested ClientProxy and ServerProxy classes. + +Build 1.8.9-11.15.0.1670: + mattmess1221: Add links for messages and other commands + +Build 1.8.9-11.15.0.1669: + fry: Reverted Render registration changes due to them working. + +Build 1.8.9-11.15.0.1668: + fry: Fixed RenderingRegistry not working. Closes #2312. + fry: Nobody is left behind. + +Build 1.8.9-11.15.0.1666: + cpw: Fix that the dummy registry entries don't allow clients connecting to servers. Also fix that it repeatedly nags about missing stuff in the world. + +Build 1.8.9-11.15.0.1665: + LexManos: Fix boss health bar rendering when debug overlay is enabled. Closes #2328 + +Build 1.8.9-11.15.0.1664: + LexManos: Allow for default 1.8.8 mods to be loaded. Should be SRG compatible. + LexManos: Fix J6 compile issue with ModelFluid. + LexManos: Swap Mod and Realms button to fix new realms 'notification' icon. + +Build 1.8.9-11.15.0.1663: + fry: Restored transparency to the Forge logo. + +Build 1.8.9-11.15.0.1662: + fry: Updated the forge logo to a higher-quality GIF file. Closes #2276. + +Build 1.8.9-11.15.0.1661: + fry: Fixed broken leaky caching of OBJ models. + fry: Fixed wrong import. + +Build 1.8.9-11.15.0.1659: + fry: Caching of fluid models. Fixes #2145. + +Build 1.8.9-11.15.0.1658: + fry: TESRs registering in preinit don't break pistons anymore. Fixes #2298. + +Build 1.8.9-11.15.0.1657: + fry: Fixed color disabling not working for forge pipeline. Closes #2286. + +Build 1.8.9-11.15.0.1656: + fry: 1.8.9 update + +Build 1.8.8-11.15.0.1655: + mezz: Allow server to access Potion isBadEffect() + cpw: 1.8.8 initial work + cpw: regenerate reference patchset at patches.mcp. These will be used to generate a new patchtree under patches. + cpw: Test of mcp patching + cpw: + Patches and rejected patches. Note: some which had imports are not listed here because they need + to be refactored not to have imports. + Progress: https://gist.github.com/cpw/29695e426e2b122cf8ff + fry: Updated various rendering-related patches and classes to 1.8.8; forge still uses vecmath. + fry: Fix generics and error in the BlockState patch. + fry: FontRenderer + Item patch update; WorldVertexBufferUploader patch derp fix. + fry: IntegratedServer, World, EnumChatFormatting and EnumFacing + fry: WorldProvider, WorldType, WorldServer, WorldServerMulti + fry: EntityAIAttackOnCollide, ServerStatusResponse, MinecraftServer, NetHandlerLoginServer, Vec3, BiomeGenBase, AnvilChunkLoader, ChunkProviderServer, MapGenRavine, MinecraftServer updated; fix in Block, GuiIngameForge; can load the world and play with ~20 more manual error fixes. + fry: BlockOre, BlockRotatedPillar, SoundManager, EntityPlayerSP, GuiScreen, GuiAchievements, GuiContainer, BlockModelRenderer, ContainerEnchantment, Slot updated manually and imports removed; GitSlot patch updated. + cpw: Delete rejects that I initially processed. + cpw: + LoadingScreenRenderer,Minecraft,GuiContainerCreative,PlayerControllerMP,LanguageManager,Locale,SimpleReloadableResourceManager,EntityList,EntityLivingBase,EntityWither,EntityMinecart,EntityPlayerMP + + NethanlderPlayClient patch is now in vanilla. The FMLCommonHandler future exception catcher is not needed anymore, it's in Util. The caching of player profiles is now in vanilla (SkinManager). + fry: Updated FML Gui methods to WorldRenderer API changes. + fry: EntityPlayer patch updated. + fry: GuiButton, GuiChat, GuiCreateWorld, GuiIngameMenu, ServerListEntryNormal, GuiStats rejects updated. + fry: RenderEntityItem, RenderPlayer, RendererLivingEntity, LayerArmorBase, ItemStack rejects fixed; changed Armor Layer hook to catch LayerBipedArmor only. + fry: ItemRenderer, RenderGlobal, StateMap, RenderItem rejects updated. + fry: TileEntity and TileEntityHopper rejects fixed, GuiOverlayDebug patch fixed. + fry: PlayerManager, PlayerProfileCache, ServerConfigurationManager, NetHandlerHandshakeTCP rejects fixed; ItemInWorldManager patch error fixed. + fry: ItemBlock, ItemBow, ItemMonsterPlacer, ItemReed, ItemSign rejects fixed. + fry: Manually updated WorldChunkManager, Chunk and BiomeDecorator patches, removed imports. + fry: TextureMap, TextureManager and Stitcher rejects fixed. + fry: EntityRenderer, StringTranslate rejects fixed. + fry: Overlay patch fix: use the correct block position. + cpw: + Enchantment,EnchantmentHelper,EntityEnderman,EntityZombie,EntityVillager,FurnaceRecipes,RecipeFireworks,RecipeRepairItem,NetworkSystem,S00PacketServerInfo + PotionEffect,StatList,Session,WeightedRandomChestContent,Explosion,ExtendedBlockStorage,SaveHandler. + + All NBT patches seem to have been merged upstream, so removing. The IntegratedServer and MinecraftServer pending queue changes also removed, as fixed upstream. + cpw: WorldGen rejects, lots of them. Couple of small fixes elsewhere. + cpw: Fix a newly missing AT. Remove all CL_ references. OBFID is gone. + cpw: All the patches done? Some code fixups.. It begins. + cpw: A few tweaks, things are starting to work now.. + fry: Model stuff: updated to generics, fixed various warnings, added handling of new perspective types. + cpw: Some generic and other warning cleanups + fry: ExtendedBlockState, Properties generic updates. + cpw: Remove a suppression + cpw: This should be everything needed to separate blocks and items completely. + fry: Implemented interpolation of TRSR transformations; B3D: added interpolation capabilities to B3DState, animated TESR example in ModelAnimationTest (pure TESR right now, no separation inside the example model between the static and dynamic parts right now). + kashike: Use the FMLSecurityManager checkPermission(Permission) method for context-based permission checks. Fixes #2067 + fry: B3D loader: removed 2 redundand null checks, changed constructor args to final to fix (java6?) inner arg error. + fry: Fix holes in generated item models. + fry: Removed face doubles from item models. + minecrell: Add jline-based console with colors and tab-completion + cpw: Fix a typesig that was broken + cpw: Re-add trove and vecmath. Mojang no longer ship them. + cpw: Use the 2.1 snapshot for FG + fry: Fixed forge lighting working incorrectly outside 0x1000000 coordinates. + cpw: Back to srgnames for patches. We should be starting to look OK now. + cpw: Small tweak to the OUT/ERR logger - should skip the Throwable stuffs now. + cpw: + Fix weird patch issue where the this FG commit: https://github.com/MinecraftForge/ForgeGradle/commit/2f0ca9921b961133689d29b807333241010a802d + breaks if the exact end of a line is a srgname. Not ideal, but should work. + cpw: Fix derpy fir trees in the taiga. So many patches. See if we can find any more mistakes?! + fry: Fixed NPE is B3D loader caused by the missing vertex normal; updated the example chest model - it's now has 2 meshes; Updated ModelAnimationDebug - it now uses the new chest model, renders the base with the static world renderer, and only the lid with the TESR. + cpw: Update mappings to 20151122. + LexManos: Update universal manifest for 1.8.8's json. + cpw: Cleanup Fluid deprecations stuff. + cpw: + OMG! Documentation? WUT? I haz lost my mind. + Also, Functional interface for IMC. Senders can send a classname implementing Guava's function, and receivers + will be able to get that function, and do, well, whatever, really. Probably best for those callback type + scenarios, connecting up APIs and stuffs. + cpw: Fix MDK for 1.8.8 using FG2.1 snapshot + cpw: Yeah, amount is NOT deprecated. + cpw: ONE EVENT BUS TO RULE THEM ALL AND IN THE DARKNESS FIRE THEM! + cpw: Deprecate it as well, because hey, it's redundant now. + cpw: Clean up some very long dead code. Bukkit hasn't existed in a very long time now. IASM never worked, and I'm not about to implement it. + cpw: Cleaning up some derpy names + cpw: More cleanups, some documentation, a bunch of deprecations. + lordillyohs: + Remove trailing */ in the build.gradle + + Signed-off-by: Anthony Anderson + cpw: Fix button and lever placement problem. Closes #2204 + cpw: Fix up a couple of patches, affected by the recent update + cpw: + Add .exc for StatList patch + + More tweaking + lumien231: Fixed: The Integrated Server not being stopped when exiting a singleplayer world to the main menu + cpw: Propogate AbortException. Closes #2206. Also fix tracing printstream when printStackTrace is called. + LexManos: Bump version for new Minecraft version. + LexManos: Fixed placing blocks on snow layers with more then one layer. + LexManos: Fixed Large mushrooms generating incorrectly. + LexManos: Removed IItemRenderer class, all functionality is possible with new rendering system. + LexManos: Compiler warnings pass, undeprecated SplashProgress related stuff. + cpw: Giant registry fixup + LexManos: Add chunk loading protection to WorldSever.getTileEntitiesIn, may prevent orphanced chunks and a CME in EntityPlayerMP. Note: The 'max' parameters are NON-inclusive. + cpw: Fix up blockstate rebuild. Closes #2221. Also fix formatting. IDEA has differences. Solved now. + bernhard.bonigl: Add a PotionRegistry to handle dynamic distribution and remapping of Potion IDs + cpw: Fix problem with spam from registry on loading a second world. Empty the staging. + cpw: Some tweaks - GameRegistry is modder facing API, so avoid having MC methods there. Fix a couple of registry bugs. + cpw: Fixup Jline integration + cpw: A few fixes + cpw: Allow entity selectors to select "." in entity names. Closes #2125 + cpw: Be noisy when API is in a coremod. It'll never work and modders should realize that fact. + cpw: + Fix language adapter loading. + + The language adapter is now properly picked up after the mod is loaded + on the classpath, fixing the ClassNotFoundException occurring before. + Also fixed some minor formatting and made it throw a full + RuntimeException on failure. + + Fix up some formatting + cpw: Most requested feature of all time? TileEntity init method called after it's ready to roll. remove all the if (firstTicks) + mark.a.woodman: + Initialize sources list + + Fixes crash NPE thrown by addFile() + cpw: Clean up chunk patch. + liach: Fix typo in `guava` + cpw: Add a flag to the modidremapping event. If the remapevent is because the registry is refreezing, it'll be true. + minecrell: + Improve the console command completer + + - Fix space after command getting removed when completing a + subcommand together with the command prefix + - Add support for completing without input (shows command list) + - Sort command completion results + - Fix console spamming command prefixes after closing the input stream + bernhard.bonigl: Add shouldRender() to Potions that allows to hide them completely in the inventory + LexManos: Remove MCP reference patches. + LexManos: Fixed colored leather armor, and custom armor textures. + fry: Fixed orientations of generated item faces. Fixes #2215. + fry: Workaround for https://github.com/google/guava/issues/738 + bx9j52xd: Fixed that oldLight is not being used. + LexManos: Fixed ModList GUI rendering incorrectly. Closes #2254 + LexManos: Add EMERALD and SILVERFISH to GenerateMinable event. Closes #1158 + LexManos: Deprecate int IDs in FluidRegistry. Modders should only ever use the String name. Also add a 'friendly' exception when attempting to get an ID for a unregistered fluid. Closes #1374 + LexManos: Fix wrong EventType passed for Emerald ore-gen. + AlgorithmX2: + Added doesSideBlockRendering to provide finer grain face culling. + Implemented for BlockStairs and BlockSlab. + cpw: Use ItemStack.hasEffect. closes #2230 + minzmann: Update PotionEffect.java.patch + cpw: Fix derpage when loading a 1.8 world, with persistent state being entirely ignored in that case. + AlgorithmX2: Add getHighlightTip allowing a item to override its displayed renderToolHightlight. + cpw: + A test for issue #1848. Please try this with any mods you can @ 1.8.8, and see if you still get log spam of any kind (not just the + log message from the issue either). + AlgorithmX2: Added addLandingEffects allowing mods to override landing particles, for blocks that require world information to determine textures. + LexManos: Fix enchanting applying the same enchantment multiple times. Closes #2273 + fry: + Preparations for the Animation system. + Changes to the Model API - IModelState now works with Optional. Handling of parts of the model is not optional, and coordinate space/result interpretation is up to the caller. IModel doesn't extend IModelPart by default anymore; MapModelState uses composition to achieve previous functionality, IModelPart implementations are disjoint now. Updated perspective handing to the new API, removed IPerspectiveState (MapModelState is now the same thing). Perspective transforms for the default fluid model. + fry: IModel can now depend on a variant definition (ModelResourceLocation); added MultiLayerModel - simple model that'll render correctly in multiple layers + example of using it. + fry: Fixed java6 errors in previous commit. + fry: Forge pipeline will now take original model lightmap into account, if present. + fry: Delayed quad list resolution in MultiModel, fixes NPE in MultiLayerModel. + fry: LightUtil.pack and .unpack now work correctly with unpacked arrays of size <4. + fry: Fix dependency resolution for models with custom data/textures. + jadran.kotnik: Fix client side commands adding parts of the color codes on autocomplete (prefix "7" and suffix "r"). + fry: Fixed NPE caused by accessing undefined layer in MultiLayerModel. + bernhard.bonigl: + Add a dynamic bucket model that displays the animated liquid contained + Has a config option (default off) that replaces the vanilla buckets with the forge bucket model + + New original bucket textures from mr_hazard + LexManos: + Fixed registry issues that prevented connecting to 1.8 Forge servers. + More precisely: Servers with missing registries default back to frozen version. + Throw descriptive error if we do not have any information. + LexManos: Fixed vanilla bug related to spawning entities on top of fences. Closes #2303 + cpw: Fix the channel handler naming. It now uses the standard netty namer for it, by careful use of cunning reflection. + cpw: Fix up persistent substitution. Should close #2259 + cpw: + Blocks are no longer erased from the registry if the mod isn't present. This means that modded blocks can potentially retain their IDs + even if they are temporarily not present in the game. Currently TileEntity data associated with the block is erased. + cpw: Try and fix registry NPE when substitution is active. + cpw: Capture ItemBlock remaps. + cpw: Actually use the delegate for the itemblock + cpw: Try and make sure active substitutions are immediately available in the block to item map. + fry: Added an ability to register custom item variants, not ending with "#inventory". Should allow grouping multiple item models into 1 blockstate json. + fry: Fixed random block position offset not applying correctly. + LexManos: Fix Button/Torch/Lever placement on stairs and slabs. Closes #2291 + diesieben07: Allow forge-type spawn-eggs to spawn child entities when clicking EntityAgeable + bernhard.bonigl: Add a hook for custom particle spawning for slimes + bernhard.bonigl: + Fix bucket replacement not loading the bucket model by itself if replacing buckets. + This happens when no other mod that uses the bucket model is present. + Also added a simple method for registering the bucket model, should give + modders an idea on how to use the general model. + +Build 1.8.8-11.15.0.1654-1.8.8: + bernhard.bonigl: + Fix bucket replacement not loading the bucket model by itself if replacing buckets. + This happens when no other mod that uses the bucket model is present. + Also added a simple method for registering the bucket model, should give + modders an idea on how to use the general model. + +Build 1.8.8-11.15.0.1653-1.8.8: + bernhard.bonigl: Add a hook for custom particle spawning for slimes + +Build 1.8.8-11.15.0.1652-1.8.8: + diesieben07: Allow forge-type spawn-eggs to spawn child entities when clicking EntityAgeable + +Build 1.8.8-11.15.0.1651-1.8.8: + LexManos: Fix Button/Torch/Lever placement on stairs and slabs. Closes #2291 + +Build 1.8.8-11.15.0.1650-1.8.8: + fry: Fixed random block position offset not applying correctly. + +Build 1.8.8-11.15.0.1649-1.8.8: + fry: Added an ability to register custom item variants, not ending with "#inventory". Should allow grouping multiple item models into 1 blockstate json. + +Build 1.8.8-11.15.0.1647-1.8.8: + cpw: Try and make sure active substitutions are immediately available in the block to item map. + +Build 1.8.8-11.15.0.1646-1.8.8: + cpw: Actually use the delegate for the itemblock + +Build 1.8.8-11.15.0.1645-1.8.8: + cpw: Capture ItemBlock remaps. + +Build 1.8.8-11.15.0.1644-1.8.8: + cpw: Try and fix registry NPE when substitution is active. + +Build 1.8.8-11.15.0.1643-1.8.8: + cpw: + Blocks are no longer erased from the registry if the mod isn't present. This means that modded blocks can potentially retain their IDs + even if they are temporarily not present in the game. Currently TileEntity data associated with the block is erased. + +Build 1.8.8-11.15.0.1642-1.8.8: + cpw: Fix up persistent substitution. Should close #2259 + +Build 1.8.8-11.15.0.1641-1.8.8: + cpw: Fix the channel handler naming. It now uses the standard netty namer for it, by careful use of cunning reflection. + +Build 1.8.8-11.15.0.1640-1.8.8: + LexManos: Fixed vanilla bug related to spawning entities on top of fences. Closes #2303 + +Build 1.8.8-11.15.0.1639-1.8.8: + LexManos: + Fixed registry issues that prevented connecting to 1.8 Forge servers. + More precisely: Servers with missing registries default back to frozen version. + Throw descriptive error if we do not have any information. + +Build 1.8.8-11.15.0.1638-1.8.8: + bernhard.bonigl: + Add a dynamic bucket model that displays the animated liquid contained + Has a config option (default off) that replaces the vanilla buckets with the forge bucket model + + New original bucket textures from mr_hazard + +Build 1.8.8-11.15.0.1637-1.8.8: + fry: Fixed NPE caused by accessing undefined layer in MultiLayerModel. + +Build 1.8.8-11.15.0.1636-1.8.8: + jadran.kotnik: Fix client side commands adding parts of the color codes on autocomplete (prefix "7" and suffix "r"). + +Build 1.8.8-11.15.0.1635-1.8.8: + fry: Fix dependency resolution for models with custom data/textures. + +Build 1.8.8-11.15.0.1634-1.8.8: + fry: LightUtil.pack and .unpack now work correctly with unpacked arrays of size <4. + +Build 1.8.8-11.15.0.1633-1.8.8: + fry: Delayed quad list resolution in MultiModel, fixes NPE in MultiLayerModel. + +Build 1.8.8-11.15.0.1632-1.8.8: + fry: IModel can now depend on a variant definition (ModelResourceLocation); added MultiLayerModel - simple model that'll render correctly in multiple layers + example of using it. + fry: Fixed java6 errors in previous commit. + fry: Forge pipeline will now take original model lightmap into account, if present. + +Build 1.8.8-11.15.0.1630-1.8.8: + LexManos: Fix enchanting applying the same enchantment multiple times. Closes #2273 + fry: + Preparations for the Animation system. + Changes to the Model API - IModelState now works with Optional. Handling of parts of the model is not optional, and coordinate space/result interpretation is up to the caller. IModel doesn't extend IModelPart by default anymore; MapModelState uses composition to achieve previous functionality, IModelPart implementations are disjoint now. Updated perspective handing to the new API, removed IPerspectiveState (MapModelState is now the same thing). Perspective transforms for the default fluid model. + +Build 1.8.8-11.15.0.1628-1.8.8: + AlgorithmX2: Added addLandingEffects allowing mods to override landing particles, for blocks that require world information to determine textures. + +Build 1.8.8-11.15.0.1627-1.8.8: + AlgorithmX2: Add getHighlightTip allowing a item to override its displayed renderToolHightlight. + +Build 1.8.8-11.15.0.1626-1.8.8: + cpw: + A test for issue #1848. Please try this with any mods you can @ 1.8.8, and see if you still get log spam of any kind (not just the + log message from the issue either). + +Build 1.8.8-11.15.0.1625-1.8.8: + minzmann: Update PotionEffect.java.patch + +Build 1.8.8-11.15.0.1624-1.8.8: + cpw: Fix derpage when loading a 1.8 world, with persistent state being entirely ignored in that case. + +Build 1.8.8-11.15.0.1623-1.8.8: + bernhard.bonigl: Add shouldRender() to Potions that allows to hide them completely in the inventory + +Build 1.8.8-11.15.0.1622-1.8.8: + minecrell: + Improve the console command completer + + - Fix space after command getting removed when completing a + subcommand together with the command prefix + - Add support for completing without input (shows command list) + - Sort command completion results + - Fix console spamming command prefixes after closing the input stream + +Build 1.8.8-11.15.0.1621-1.8.8: + cpw: Use ItemStack.hasEffect. closes #2230 + +Build 1.8.8-11.15.0.1620-1.8.8: + liach: Fix typo in `guava` + +Build 1.8.8-11.15.0.1619-1.8.8: + AlgorithmX2: + Added doesSideBlockRendering to provide finer grain face culling. + Implemented for BlockStairs and BlockSlab. + +Build 1.8.8-11.15.0.1618-1.8.8: + LexManos: Fix wrong EventType passed for Emerald ore-gen. + +Build 1.8.8-11.15.0.1617-1.8.8: + LexManos: Deprecate int IDs in FluidRegistry. Modders should only ever use the String name. Also add a 'friendly' exception when attempting to get an ID for a unregistered fluid. Closes #1374 + +Build 1.8.8-11.15.0.1616-1.8.8: + LexManos: Add EMERALD and SILVERFISH to GenerateMinable event. Closes #1158 + +Build 1.8.8-11.15.0.1615-1.8.8: + LexManos: Fixed ModList GUI rendering incorrectly. Closes #2254 + +Build 1.7.10-10.13.4.1614-1.7.10: + LexManos: Fix dispensers equipping armor on players in the wrong slot Closes #1649 + +Build 1.8.8-11.15.0.1613-1.8.8: + bx9j52xd: Fixed that oldLight is not being used. + +Build 1.8.8-11.15.0.1612-1.8.8: + fry: Workaround for https://github.com/google/guava/issues/738 + +Build 1.8.8-11.15.0.1611-1.8.8: + fry: Fixed orientations of generated item faces. Fixes #2215. + +Build 1.8.8-11.15.0.1610-1.8.8: + LexManos: Remove MCP reference patches. + LexManos: Fixed colored leather armor, and custom armor textures. + +Build 1.8.8-11.15.0.1609-1.8.8: + cpw: Add a flag to the modidremapping event. If the remapevent is because the registry is refreezing, it'll be true. + +Build 1.8.8-11.15.0.1608-1.8.8: + cpw: Clean up chunk patch. + +Build 1.8.8-11.15.0.1607-1.8.8: + mark.a.woodman: + Initialize sources list + + Fixes crash NPE thrown by addFile() + +Build 1.8.8-11.15.0.1606-1.8.8: + cpw: Most requested feature of all time? TileEntity init method called after it's ready to roll. remove all the if (firstTicks) + +Build 1.8.8-11.15.0.1605-1.8.8: + cpw: + Fix language adapter loading. + + The language adapter is now properly picked up after the mod is loaded + on the classpath, fixing the ClassNotFoundException occurring before. + Also fixed some minor formatting and made it throw a full + RuntimeException on failure. + + Fix up some formatting + +Build 1.8.8-11.15.0.1604-1.8.8: + cpw: Be noisy when API is in a coremod. It'll never work and modders should realize that fact. + +Build 1.8.8-11.15.0.1603-1.8.8: + mezz: Allow server to access Potion isBadEffect() + +Build 1.8.8-11.15.0.1602-1.8.8: + kashike: Use the FMLSecurityManager checkPermission(Permission) method for context-based permission checks. Fixes #2067 + +Build 1.8.8-11.15.0.1601-1.8.8: + cpw: Allow entity selectors to select "." in entity names. Closes #2125 + +Build 1.8.8-11.15.0.1600-1.8.8: + minecrell: Add jline-based console with colors and tab-completion + bernhard.bonigl: Add a PotionRegistry to handle dynamic distribution and remapping of Potion IDs + cpw: Some tweaks - GameRegistry is modder facing API, so avoid having MC methods there. Fix a couple of registry bugs. + cpw: Fixup Jline integration + cpw: A few fixes + +Build 1.8.8-11.15.0.1596-1.8.8: + cpw: Fix problem with spam from registry on loading a second world. Empty the staging. + +Build 1.8.8-11.15.0.1595-1.8.8: + lumien231: Fixed: The Integrated Server not being stopped when exiting a singleplayer world to the main menu + +Build 1.8.8-11.15.0.1594-1.8.8: + LexManos: Add chunk loading protection to WorldSever.getTileEntitiesIn, may prevent orphanced chunks and a CME in EntityPlayerMP. Note: The 'max' parameters are NON-inclusive. + cpw: Fix up blockstate rebuild. Closes #2221. Also fix formatting. IDEA has differences. Solved now. + +Build 1.8.8-11.15.0.1592-1.8.8: + cpw: Giant registry fixup + +Build 1.8.8-11.15.0.1591-1.8.8: + LexManos: Bump version for new Minecraft version. + LexManos: Fixed placing blocks on snow layers with more then one layer. + LexManos: Fixed Large mushrooms generating incorrectly. + LexManos: Removed IItemRenderer class, all functionality is possible with new rendering system. + LexManos: Compiler warnings pass, undeprecated SplashProgress related stuff. + +Build 1.8.8-11.14.4.1590-1.8.8: + cpw: Propogate AbortException. Closes #2206. Also fix tracing printstream when printStackTrace is called. + +Build 1.8.8-11.14.4.1589-1.8.8: + cpw: Fix up a couple of patches, affected by the recent update + cpw: + Add .exc for StatList patch + + More tweaking + +Build 1.8.8-11.14.4.1588-1.8.8: + cpw: Fix button and lever placement problem. Closes #2204 + +Build 1.8.8-11.14.4.1587-1.8.8: + lordillyohs: + Remove trailing */ in the build.gradle + + Signed-off-by: Anthony Anderson + +Build 1.8.8-11.14.4.1586-1.8.8: + cpw: Cleaning up some derpy names + cpw: More cleanups, some documentation, a bunch of deprecations. + +Build 1.8.8-11.14.4.1585-1.8.8: + cpw: Deprecate it as well, because hey, it's redundant now. + cpw: Clean up some very long dead code. Bukkit hasn't existed in a very long time now. IASM never worked, and I'm not about to implement it. + +Build 1.8.8-11.14.4.1584-1.8.8: + cpw: ONE EVENT BUS TO RULE THEM ALL AND IN THE DARKNESS FIRE THEM! + +Build 1.8.8-11.14.4.1583-1.8.8: + cpw: Fix MDK for 1.8.8 using FG2.1 snapshot + cpw: Yeah, amount is NOT deprecated. + +Build 1.8.8-11.14.4.1582-1.8.8: + cpw: + OMG! Documentation? WUT? I haz lost my mind. + Also, Functional interface for IMC. Senders can send a classname implementing Guava's function, and receivers + will be able to get that function, and do, well, whatever, really. Probably best for those callback type + scenarios, connecting up APIs and stuffs. + +Build 1.8.8-11.14.4.1581-1.8.8: + cpw: Cleanup Fluid deprecations stuff. + +Build 1.8.8-11.14.4.1580-1.8.8: + LexManos: Update universal manifest for 1.8.8's json. + +Build 1.8.8-11.14.4.1579-1.8.8: + fry: Fixed NPE is B3D loader caused by the missing vertex normal; updated the example chest model - it's now has 2 meshes; Updated ModelAnimationDebug - it now uses the new chest model, renders the base with the static world renderer, and only the lid with the TESR. + LexManos: Fix incorrect position passes to Block.getExplosionResistance from entities. + LexManos: Moved client side Block.onBlockDestroyed to after Item.onBlockDestroyed to match server order. + LexManos: Enable the normal ModList GUI in game. Use GL_SCISSOR to support the transparent in-game GUI. + Choonster.2010: BiomeManager: Fix off-by-one errors + LexManos: Fix bold font rendering, Unicode is 2x pixel density of normal. + LexManos: Fix invalid position passed to isAir/getLight in World.playAmbientSound + fry: Fixed forge lighting working incorrectly outside 0x1000000 coordinates. + fry: Implemented interpolation of TRSR transformations; B3D: added interpolation capabilities to B3DState, animated TESR example in ModelAnimationTest (pure TESR right now, no separation inside the example model between the static and dynamic parts right now). + fry: B3D loader: removed 2 redundand null checks, changed constructor args to final to fix (java6?) inner arg error. + fry: Fix holes in generated item models. + fry: Removed face doubles from item models. + fry: Fixed forge lighting working incorrectly outside 0x1000000 coordinates. + cpw: Update mappings to 20151122. + +Build 1.8-11.14.4.1577: + fry: Implemented interpolation of TRSR transformations; B3D: added interpolation capabilities to B3DState, animated TESR example in ModelAnimationTest (pure TESR right now, no separation inside the example model between the static and dynamic parts right now). + fry: B3D loader: removed 2 redundand null checks, changed constructor args to final to fix (java6?) inner arg error. + fry: Fix holes in generated item models. + fry: Removed face doubles from item models. + fry: Fixed forge lighting working incorrectly outside 0x1000000 coordinates. + +Build 1.8.8-11.14.4.1576-1.8.8: + cpw: Fix derpy fir trees in the taiga. So many patches. See if we can find any more mistakes?! + +Build 1.8.8-11.14.4.1575-1.8.8: + cpw: Small tweak to the OUT/ERR logger - should skip the Throwable stuffs now. + cpw: + Fix weird patch issue where the this FG commit: https://github.com/MinecraftForge/ForgeGradle/commit/2f0ca9921b961133689d29b807333241010a802d + breaks if the exact end of a line is a srgname. Not ideal, but should work. + +Build 1.8-11.14.4.1572: + LexManos: Fix invalid position passed to isAir/getLight in World.playAmbientSound + +Build 1.8-11.14.4.1571: + Choonster.2010: BiomeManager: Fix off-by-one errors + +Build 1.8-11.14.4.1570: + LexManos: Fix bold font rendering, Unicode is 2x pixel density of normal. + +Build 1.8-11.14.4.1569: + LexManos: Enable the normal ModList GUI in game. Use GL_SCISSOR to support the transparent in-game GUI. + +Build 1.8-11.14.4.1568: + LexManos: Fix incorrect position passes to Block.getExplosionResistance from entities. + LexManos: Moved client side Block.onBlockDestroyed to after Item.onBlockDestroyed to match server order. + +Build 1.7.10-10.13.4.1566-1.7.10: + scott: Add an event hook to allow overriding of fuels recognized by vanilla fuel handling. + +Build 1.8-11.14.4.1565: + lumien231: Fix Client Login Issue when logging into a non existent dimension + +Build 1.7.10-10.13.4.1564-1.7.10: + LexManos: Fixed skulls not placing on fences like vanilla {Vanilla is buggy -.-} Closes #2185 + +Build 1.8-11.14.4.1563: + LexManos: Bump version for Recomended Build. + +Build 1.8-11.14.3.1562: + LexManos: + Merge pull request #2179 from arideus101/patch-1 + + Fixed issue where custom colored armor wouldn't be colored. (reverted from commit dee0b2084b519419bbf97d8ad177204830ea2b07) + +Build 1.8-11.14.3.1561: + jasondoyle528: Allows Custom Armor Coloring to be done easily + +Build 1.8-11.14.3.1560: + gabizou: + Fix a possible NPE when checking supertypes of interfaces. Closes #2176. + + Signed-off-by: Gabriel Harris-Rouquette + +Build 1.8-11.14.3.1559: + bjoern: + Fix possible crash in EventBus + + There is currently no way to check if an event handler has been registered or not. + But when trying to unregister a not-registered event handler, Minecraft crashes with a NullPointerException. + This is a simple fix to prevent such crashes. + cpw: Fix substitutions for recipes and oredict recipes. Should mean that substitutions start working properly. + cpw: + OreDictionary will warn if there's an invalid ore being registered now, rather than just + using -1 and doing weird things with the list as a result. + cpw: Two more corner cases in the oredictionary. Should work for all cases now. + cpw: + Fix firing the remap event. It always fires now, and additionally fires when the registry reverts to frozen. + Most mods refer to the gameregistry for ids they care about, so this shouldn't affect anything significantly, + but if your mod was dependent on their being content in the remap event, and only acting on that content, + empty content means it's "reverted to frozen" state - the state at the start of the game. + +Build 1.7.10-10.13.4.1558-1.7.10: + cpw: + Fix firing the remap event. It always fires now, and additionally fires when the registry reverts to frozen. + Most mods refer to the gameregistry for ids they care about, so this shouldn't affect anything significantly, + but if your mod was dependent on their being content in the remap event, and only acting on that content, + empty content means it's "reverted to frozen" state - the state at the start of the game. + +Build 1.7.10-10.13.4.1557-1.7.10: + cpw: Two more corner cases in the oredictionary. Should work for all cases now. + +Build 1.8-11.14.3.1556: + LexManos: Fixed Open url confirm screen not showing URL. + LexManos: Fixed Stronghold Library not having anything in it's chests. + +Build 1.8-11.14.3.1555: + diesieben07: Fix GameData.findBlock + +Build 1.8-11.14.3.1554: + fry: Fixed anaglyph transformation not being applied in the forge lighting fully. + +Build 1.8-11.14.3.1553: + fry: Provide ItemCameraTransforms for vanilla models when possible. + +Build 1.8-11.14.3.1552: + fry: OBJ loader: reworked texture resolution: keys now have to start with #, like every other model loader; models without explicit library now work, remapping is possible by using the key "#OBJModel.Default.Texture.Name"; in addition to remapping by material name, remapping by texture name works too, like in other model formats. + +Build 1.8-11.14.3.1551: + fry: Performace fix for item rendering. + +Build 1.8-11.14.3.1550: + Choonster.2010: + Fix texture error message for broken textures + + -- Fixes #2100 + -- Iterates over badTextureDomains instead of missingTextures.keySet() + as a domain can have broken textures without any missing textures + +Build 1.8-11.14.3.1549: + fry: Obj loader: fix vertices shared between faces having the same attributed (uvs/normals). + the.f1repl4ce: Fixed a bug that caused the config option name to overlap with the selectable values when using GuiConfigEntries.SelectValueEntry, fixes #2114 + fry: OBJ model: use original vertex material when defining face. + fry: Fixed block color multiplier not being cached properly in the forge renderer, performance improvement. + LexManos: Merge FML and Forge lang file, and update crowdin project. + LexManos: + Redesign the ModList GUI to use a scrolling list for the main body content. + Allowing for larger information to be displayed. + URLs are auto-detected and now clickable. + Mod Logos are now centered, it looks better. + LexManos: + Introduce a new centralized version checking system. + Using the @Mod annotation mods can opt-in to a centrally controlled update system. + This is PURELY a notification system and will NOT automatically download any updates. + The End User can control which mods check for updates and disabel the system entirely using the Forge Config and GUI. + Format for the json the URL must point to is described here: https://gist.github.com/LexManos/7aacb9aa991330523884 + +Build 1.8-11.14.3.1543: + fry: Fix color multiplier applied incorrectly for items. + fry: OBJ loader: fixed another whitespace-related issue; removed unused "modifyUVs" property for now; added the "flip-v" property to switch between OpenGL-style and DirextX-style model UVs; fixed normals - they are now correct in-world, still a bit strange for the items; fixed normals a little bit for B3D models too. + +Build 1.7.10-10.13.4.1541-1.7.10: + cpw: + OreDictionary will warn if there's an invalid ore being registered now, rather than just + using -1 and doing weird things with the list as a result. + +Build 1.7.10-10.13.4.1539-1.7.10: + diesieben07: Fix CME when entities are spawned from EntityJoinWorldEvent + bernhard.bonigl: Fix Potion IDs above 127 + Abrar Syed: Update FG version to 2.0.2 + fry: Changed how the forge lighting system handles holey models for opaque blocks; it now mimics vanilla behaviour, which allows light to pass through them. + fry: Fix for the previous commit - transparent blocks were handled improperly. + glstillman: OBJLoader: Quick bandages to support/fix the new way that face/vertex normals are calculated, a very quick bandage to patch TextureCoordinates for the time being, and the Parser now uses a Pattern to split strings on whitespace instead of only splitting on " ". + cpw: Fix substitutions for recipes and oredict recipes. Should mean that substitutions start working properly. + +Build 1.8-11.14.3.1532: + fry: Fixed normal calculation for vanilla models, fixed the grass darkening and simular issues. + fry: More lighting fixes, flat lighting now works correctly for grass and torches. + +Build 1.8-11.14.3.1530: + fry: Fixed piston rendering (WorldRenderer offset wasn't applied); fixed TESR being registered too early in one of the debug mods. + +Build 1.8-11.14.3.1529: + glstillman: Fixed a bug with item model loading that would occur if ModelBakery.addVariantName() was called with the same string location parameter for 2 different items, and the string pointed to a location that didn't exist, where ModelLoader.loadAnyModel() would substitute the blockdefinition in for the item model, but wouldn't remove the original input location from the loadingModels list, which would cause the location from the second call to throw an IllegalStateException even though that location now has a model. + fry: + Perspective awareness for vanilla and multi models, fixes #2148. + Improved error handling in MultiModel. + fry: Fixed generic bug in MultiModel + fry: Fixed AO being applied to OBJ model transparency, and OBJ loader trying to force the loading of the builtin white texture. + fry: Provide a IModel for "builtin/generated", fixes #2147 + fry: Fixed another generic issue in MultiModel. + +Build 1.8-11.14.3.1525: + fry: Small fix for reworked classic lighting + +Build 1.8-11.14.3.1524: + fry: Added OBJ loader for the ModelLoaderRegistry system. + +Build 1.8-11.14.3.1523: + fry: + Model pipeline system. + Should replace all ad-hoc quad generation methods in forge, and make IBakedModel -> WorldRenderer data transfer faster. Added IVertexConsumer + helper classes; lighting that works correctly for non-axis-aligned faces using the new infrastructure. Changed smooth lighting algorithm, now it should work correctly for everything. + New block lighter can be disabled in the forge config options. + fry: Added back in the lost class + +Build 1.8-11.14.3.1521: + simonbarnes1: + Add Guava and Apache to LaunchClassLoader exclusion list on server + + Move exclusions to common place. Less likely to get out of sync + +Build 1.8-11.14.3.1520: + bk1325: Add PlayerSetSpawnEvent + +Build 1.8-11.14.3.1519: + fry: '#' is now added automatically to the beginning of the texture names in B3D models, and the remapping is expected via the blockstate JSON, since it's more reasonable than adding it to the file name in the modelling program or matching the resource location with the filename. + +Build 1.8-11.14.3.1518: + fry: Fixes Attributes.transform affecting only 1 vertex. + +Build 1.7.10-10.13.4.1517-1.7.10: + bjoern: + Fix possible crash in EventBus + + There is currently no way to check if an event handler has been registered or not. + But when trying to unregister a not-registered event handler, Minecraft crashes with a NullPointerException. + This is a simple fix to prevent such crashes. + +Build 1.8-11.14.3.1516: + luacs1998: Fix a possible crash in EventBus + +Build 1.8-11.14.3.1515: + rubensworks: Fix dispenser action for modded spawn eggs + +Build 1.8-11.14.3.1514: + LexManos: Update Gradle wrapper to 2.7 + LexManos: Fix entity count being incorrect for spawning logic. Now filter out 'persistant' entities. + +Build 1.8-11.14.3.1513: + foka_12: Disallow conflicting furnace recipes + +Build 1.8-11.14.3.1512: + fry: Workaround for MinecraftForge/ForgeGradle#256 + +Build 1.8-11.14.3.1511: + cpw: + FMLNetworkHandler.openGui should not try and open a GUI on a FakePlayer. + Fixes #2082 and probably dozens of mod errors. Also, side benefit of the + merged codebase! FML code can ref Forge code! + +Build 1.8-11.14.3.1510: + laci200270: Update FMLSecurityManager.java + cpw: Format a bit better + +Build 1.8-11.14.3.1509: + fry: Much requested temporary hack for items and TESRs. Context: #1582, #1597, #1713, #2058 and others. + +Build 1.8-11.14.3.1508: + vorquel: Fix faulty Channel name + +Build 1.8-11.14.3.1507: + simonbarnes1: Use already provided profile for the player's own skin + +Build 1.8-11.14.3.1506: + cpw: + More cleanup of the default eclipse workspace. The project is now called "MDKExample" not "Minecraft". + The project tree is now contemporary, instead of a copy from 1.5.x era MC. The launches are cleaned up, and refer to + a better default "runDir" of "run" rather than "eclipse".. Updating to FG2.0.1 which will contain relevant binary fixes. + +Build 1.8-11.14.3.1505: + cpw: Fix eclipse workspace inside the mdk - don't run it through the tokenconverter. Also add in CREDITS-fml.txt to the MDK - it's still required. + +Build 1.8-11.14.3.1504: + LexManos: + Fixed issue where config folder would not be created before SplashProgress tried to read from it. + Default macs to disable the new loading screen due to to many macs having issues. + Users can enable it again by editing their config. + Catch and gracefully handle more errors when starting up the Splash Screen. + +Build 1.8-11.14.3.1503: + diesieben07: Re-introduce RenderBlockOverlayEvent, seems to have been missed during 1.8 update + Abrar Syed: removed broken and duplicate AT lines + Abrar Syed: added hardcoded fml version file + Abrar Syed: DeobfuscationData no longer required at dev time + Abrar Syed: removed old unnecessary stuff + Abrar Syed: Added FG2 buildscript + updated for Gradle 2.4 + Abrar Syed: added jenkins compat tasks + Abrar Syed: fixed local-building fail with changelog + Abrar Syed: updated installed gradle. not finished + Abrar Syed: fixed deployment credentials + cpw: FML is no more. FML has ceased to be. FML's expired and gone to meet its maker. FML's a stiff! Bereft of life, FML rests in peace. + cpw: Vestigal fml-ectomy. + cpw: + FML's metabolic processes are now history. FML's off the twig. FML's kicked the bucket, FML's shuffled off this mortal coil, + run down the curtain and joined the bleedin' choir invisible!! THIS IS AN EX-PROJECT! + Abrar Syed: added MDK package + cpw: Fix MDK task - it now runs. + cpw: The final nail in the coffin. BYE! + cpw: + Trying to fix the MDK to include gradle wrapper, but the gradle-wrapper.jar is + corrupted. @AbrarSyed can you take a look? + cpw: + Fix packaging the gradle wrapper properly. There is still a problem with + the MDK- it fails to run setupDecompWorkspace. + + Filed an issue at ForgeGradle, since this seems to be something FG2 shouldn't + be doing, but is? + + https://github.com/MinecraftForge/ForgeGradle/issues/235 + cpw: Remove patches + cpw: Fix ciWriteBuildNumber task. Ugly, but it works. + cpw: Fix crowdin task. Good luck jenkins, lets roll! + cpw: Fix crowdin again. Run, jenkins, for god's sake, run! + LexManos: Update gradle wrapper and fix changelog task. + +Build 1.8-11.14.3.1502: + cpw: ObjectHolder works great, but it should be a lot less spammy about failed lookups. They're usually mod options. + cpw: Actually rebuild the fluidNames each rebuild, don't just try and force changes in. Should fix #1973 + cpw: + Wake up the FluidRegistry before any mods start loading. Should stop mods claiming to own water or lava (depending on who accessed + FluidRegistry first) + cpw: Be a little bit more helpful when the ObjectHolder misses. Should help figure out what is going in in #2006 + cpw: Cherry pick some changes from 1.8 for inner class discovery, also fix the negativecache. Closes #1872 + +Build 1.8-11.14.3.1501: + LexManos: Fix placing skulls on fence posts. Closes #2055 + +Build 1.8-11.14.3.1500: + starbuck: Fix harvest logic running in addition to shearable logic + +Build 1.8-11.14.3.1499: + glstillman: Fixed a bug with ExtendedBlockStates containing at least one IProperty and one IUnlistedProperty not allowing blocks to be placed. + +Build 1.8-11.14.3.1498: + liach: Add an EnumHelper hook and fixed an issue + +Build 1.8-11.14.3.1497: + simonbarnes1: Fix placing signs with NBT prompting for text + +Build 1.8-11.14.3.1496: + Zaggy1024: + Fixed a Forge blockstates json removing models causing an NPE in the loader. + Fixed the deep clone of a V1 Variant not cloning the submodels properly. + +Build 1.8-11.14.3.1495: + clienthax: Signed-off-by: Clienthax + +Build 1.8-11.14.3.1494: + rubensworks: Make EnumFacing events available server-side + +Build 1.8-11.14.3.1493: + rubensworks: Fix source block check for BlockFluidClassic + +Build 1.7.10-10.13.4.1492-1.7.10: + cpw: Cherry pick some changes from 1.8 for inner class discovery, also fix the negativecache. Closes #1872 + +Build 1.8-11.14.3.1491: + diesieben07: Allow the new entity eggs to be created via middle-click + +Build 1.7.10-10.13.4.1490-1.7.10: + cpw: Actually rebuild the fluidNames each rebuild, don't just try and force changes in. Should fix #1973 + cpw: + Wake up the FluidRegistry before any mods start loading. Should stop mods claiming to own water or lava (depending on who accessed + FluidRegistry first) + cpw: Be a little bit more helpful when the ObjectHolder misses. Should help figure out what is going in in #2006 + +Build 1.8-11.14.3.1487: + fry: Custom transformations in forge blockstate json. + +Build 1.8-11.14.3.1486: + LexManos: Fixed ItemMonsterPlacer.getEggInfo missing return. Closes #1975 + +Build 1.8-11.14.3.1485: + jadran.kotnik: Fixed error GUIs showing a white screen and replaced a rogue direct GL call. + +Build 1.8-11.14.3.1484: + izooDee5: Fix particle texture of the generated item models. + +Build 1.8-11.14.3.1483: + bernhard.bonigl: Fix StateMap always mapping properties to the "minecraft" domain instead of the mods, causing it to not find BlockState definitions. + +Build 1.8-11.14.3.1482: + LexManos: Add debug for max texture size and output when Texture Atlas can not stitch all textures. + +Build 1.7.10-10.13.4.1481-1.7.10: + cpw: ObjectHolder works great, but it should be a lot less spammy about failed lookups. They're usually mod options. + +Build 1.8-11.14.3.1480: + LexManos: More descripotive error if Patcher is passed invalid data for vanilla classes. + +Build 1.8-11.14.3.1479: + fry: Fixed perspective transformations for item models. + fry: Vanilla models can now use custom textures. Fixes #1962 + fry: There's no Map.getOrDefault in java6. + +Build 1.8-11.14.3.1476: + LexManos: Fix AT for Block constructor. + +Build 1.8-11.14.3.1475: + fry: Added ItemLayerModel - less awkward, simpler and faster version of ItemModelGenerator. + +Build 1.8-11.14.3.1474: + LexManos: Throw more descriptive errors when mods attempt to register invalid global entity IDs. + LexManos: Include the thread state in the potential error handleing for SplashProgress. + LexManos: Fixed Wavefront Object Importer reading files with integer values. Closes #1651, #1654 + LexManos: Create config folder in SplashProgress if it does not exist. + LexManos: Add the stitching allocation stage to loading screen. + LexManos: + Make TextureMap for items and blocks skip the first pass of loading/stitching textures. + Should decrease loading times for large packs. + May cause issues with some mods so use -Dfml.skipFirstTextureLoad=false to disable. + LexManos: Add TextureManager to loading screen. + LexManos: Time each bar in the loading screen and print it to the log, useful information to see where most time is spent in loading. + cpw: + Mods that are extracted to the mods dir by unzipping or whatever will now cause the game to crash. Too much info is in the META-INF now, + and more will be being added. Extracting to the mods dir just completely breaks that. + +Build 1.8-11.14.3.1473: + jamioflan: + Added CameraSetup sub-event for camera angles + + Allows players to alter yaw and pitch of renderViewEntity, but more importantly, adds the ability to roll the view. + + Added camera roll hook + +Build 1.7.10-10.13.4.1472-1.7.10: + michafla: fix logic for guessing mesa tag in biome dict + cpw: + Mods that are extracted to the mods dir by unzipping or whatever will now cause the game to crash. Too much info is in the META-INF now, + and more will be being added. Extracting to the mods dir just completely breaks that. + +Build 1.7.10-10.13.4.1470-1.7.10: + LexManos: Add TextureManager to loading screen. + LexManos: Time each bar in the loading screen and print it to the log, useful information to see where most time is spent in loading. + +Build 1.7.10-10.13.4.1469-1.7.10: + LexManos: Create config folder in SplashProgress if it does not exist. + LexManos: Add the stitching allocation stage to loading screen. + LexManos: + Make TextureMap for items and blocks skip the first pass of loading/stitching textures. + Should decrease loading times for large packs. + May cause issues with some mods so use -Dfml.skipFirstTextureLoad=false to disable. + +Build 1.8-11.14.3.1468: + LexManos: Patch line number update. Ignore this. + LexManos: + New system in EntityRegistry to allow modders to register spawn eggs. + For entites that do not use the global ID system. + {Which no mod entity should} + Vanilla spawn eggs will now detect a 'entity_name' entry in it's NBT data and use that for spawning/rendering. + +Build 1.8-11.14.3.1467: + foka_12: Change permission levels on ore recipes parameters + +Build 1.8-11.14.3.1466: + foka_12: + Fixed NPE when calling canBrew + + Oversight on my part, + + If the ingredient doesn't return true in Item.isPotionIngredient, Items.potionitem.getEffects(stack) returns null, causing an NPE to be thrown later on. + + This invalidates #1947. + +Build 1.8-11.14.3.1465: + LexManos: Use Guava instead of Nio for J6 compatibility. + +Build 1.8-11.14.3.1464: + fry: Added fluid renderer. + +Build 1.8-11.14.3.1463: + LexManos: Make Item.shouldCauseReequipAnimation is bit more precise and copy over the new item for rendering even if the animation is diabled. + +Build 1.8-11.14.3.1462: + LexManos: Add vanilla block rotation support back in for certain blocks that were missed in 1.8 update. Closes #1903 + +Build 1.8-11.14.3.1461: + xxmicloxx: Bugfix for B3DLoader + +Build 1.8-11.14.3.1460: + Daniel: + Fix incorrect block position in BlockReed canPlaceBlockAt + + block.canSustainPlant is called on the wrong block position. It should be called on the block below (the 'soil' block). + +Build 1.8-11.14.3.1459: + mezz: Add GuiScreenEvents for keyboard and mouse input + +Build 1.8-11.14.3.1458: + t.tomkins: + Player sensitive version of Block.getPickBlock + + Block.getPickBlock was patched in 1.7 but was overlooked in 1.8. + + Closes: https://github.com/MinecraftForge/MinecraftForge/issues/1709 + LexManos: Add Item.shouldCauseReequipAnimation to allow modders more control over the 'Reequip' animation. + +Build 1.8-11.14.3.1457: + LexManos: Fixed compile issues with irtimaled's PR. + +Build 1.7.10-10.13.4.1456-1.7.10: + irtimaled: + Copy fortress.dat from vanilla location + + Fixes #1747 + + If the dat file isn't in the dimension specific folder but is present in the vanilla data folder then copy it over. + LexManos: Make FML Gui classes use GlStateManager. Closes FML#615 + LexManos: Fixed Wavefront Object Importer reading files with integer values. Closes #1651, #1654 + +Build 1.8-11.14.3.1453: + LexManos: Cleanup code format in LayerBreakingTest. + LexManos: Fixup model loading errors not being printed by making ICustomModelLoader.loadModel propogate IOExceptions as needed. + +Build 1.7.10-10.13.4.1452-1.7.10: + LexManos: Include the thread state in the potential error handleing for SplashProgress. + +Build 1.7.10-10.13.4.1451-1.7.10: + LexManos: Throw more descriptive errors when mods attempt to register invalid global entity IDs. + +Build 1.8-11.14.3.1450: + LexManos: Bump version for new RB. + LexManos: Cleanup some spammy output. + LexManos: Quiet ClassPatchManager debug spam by default. Reenable using -Dfml.debugClassPatchManager=true. + LexManos: Quiet FMLControlledNamespacedRegistry debug spam by default. Reenable using -Dfml.debugRegistryEntries=true. + LexManos: Quiet CrashReport class pre-loading debug, no flag to re-enable. + LexManos: Cleanup mod signature data table. Easily seperating those mods with signatures vs those with none. + LexManos: Cleanup mod state dump to be easier to read by displaying the states in abreviation and placing them before the mod info. + +Build 1.8-11.14.3.1449: + clarsson: Fixing two IndexOutBoundsExceptions from the BiomeDictionary + +Build 1.7.10-10.13.4.1448-1.7.10: + LexManos: Cleanup mod state dump to be easier to read by displaying the states in abreviation and placing them before the mod info. + +Build 1.7.10-10.13.4.1447-1.7.10: + LexManos: Cleanup some spammy output. + LexManos: Quiet ClassPatchManager debug spam by default. Reenable using -Dfml.debugClassPatchManager=true. + LexManos: Quiet FMLControlledNamespacedRegistry debug spam by default. Reenable using -Dfml.debugRegistryEntries=true. + LexManos: Quiet CrashReport class pre-loading debug, no flag to re-enable. + LexManos: Cleanup mod signature data table. Easily seperating those mods with signatures vs those with none. + +Build 1.8-11.14.3.1446: + LexManos: Copy over parent's modelSet value in variants as well. + LexManos: Bump version for new RB + +Build 1.7.10-10.13.4.1445-1.7.10: + LexManos: Bump version for new RB. + +Build 1.8-11.14.2.1444: + cpw: + Fix up the client/server fluid race condition that could cause the game to bail when an SSP game connects. + There's still a teeny gap, but it's MUCH less prominent than it was before. + +Build 1.8-11.14.2.1443: + lumien231: Added LivingExperienceDropsEvent to change the amount of experience an entity drops + +Build 1.8-11.14.2.1442: + jcoleman350: Added ItemMap type check to item frames and ItemRenderer, enabling vanilla style rendering for custom maps + +Build 1.8-11.14.2.1441: + LexManos: Redefine removal value in IRetextureableModel from null to empty string {""} due to ImmutibleMap not allowing null Values. Closes #1927 + +Build 1.8-11.14.2.1440: + foka_12: Added a brewing registry system + +Build 1.8-11.14.2.1439: + LexManos: Limit Sign text to 384 json characters and strip control codes. This is 1.8.7's Sign fix. Thanks Searge. + LexManos: Remove worlds from WorldBorder when unloaded tol prevent memory leak, Closes #1923 + LexManos: Missed patch for sign fix. + +Build 1.8-11.14.2.1437: + cpw: + Attempt to synchronize the state for vanilla client completions and quit the handler if the vanilla + thread has already setup the connection. Should fix #1924 + +Build 1.8-11.14.2.1436: + LexManos: Change custom payload lock to be buffer itself incase multiple packets use the same backend buffer. Closes #1908 + +Build 1.8-11.14.2.1435: + Fuami.cake: + Added Breaking Animation for Smart Models, Checking each layer. + Added Layered Smart Render Test. + +Build 1.8-11.14.2.1434: + LexManos: Update README.txt + +Build 1.8-11.14.2.1433: + fry: Fixed missing variant logging; added the possibility to specify the item variant in the blockstate json. + fry: fixed AT missing for getModelBlockDefinition + fry: + B3D Improvements + - fixed keyframe transformation application + - textures are now resolved the same way as in vanilla models + - added the ability to use forge blockstate texture information + - removed unused code from the B3D example + +Build 1.8-11.14.2.1431: + fry: Fixed partial variant detection in the forge blockstate loader. + +Build 1.8-11.14.2.1430: + LexManos: Mitigate potential issue with users requesting lots of server status information by caching ServerStatus json. + +Build 1.8-11.14.2.1429: + clienthax: + Add missing forge patch from 1.8 port + https://github.com/MinecraftForge/MinecraftForge/blob/1.7.10/patches/minecraft/net/minecraft/client/Minecraft.java.patch#L88 + +Build 1.7.10-10.13.3.1428-1.7.10: + cpw: + Fix up the client/server fluid race condition that could cause the game to bail when an SSP game connects. + There's still a teeny gap, but it's MUCH less prominent than it was before. + +Build 1.8-11.14.2.1427: + LexManos: + Hook BlockState's Json loading to add support for simplified Forge format. + See https://github.com/MinecraftForge/MinecraftForge/pull/1885 for more details. + +Build 1.8-11.14.2.1426: + cpw: + Attempt to disable the new splash screen if there are errors detected finishing up. Print a slightly more helpful error message. + + (cherry picked from commit e3211eec0469dc6717943010d584207b7abdb1e0) + + Conflicts: + fml/src/main/java/cpw/mods/fml/client/SplashProgress.java + cpw: + Finish loading screen before going fullscreen. Closes MinecraftForge/FML#662 + + (cherry picked from commit 19d7e16fa6a28c5665de1ed6e50d8699e865bff2) + + Conflicts: + fml/patches/minecraft/net/minecraft/client/Minecraft.java.patch + cpw: So we can't print a lot of unicode in the splash screen, so restrict to a hard subset we know we CAN print, closes #1910 + LexManos: Fixed missing import in last merge. + +Build 1.7.10-10.13.3.1424-1.7.10: + cpw: So we can't print a lot of unicode in the splash screen, so restrict to a hard subset we know we CAN print, closes #1910 + +Build 1.8-11.14.2.1423: + AlgorithmX2: Allow placing item frames on Solid Block Faces (isSideSolid) + +Build 1.7.10-10.13.3.1422-1.7.10: + cpw: + Attempt to disable the new splash screen if there are errors detected finishing up. Print a slightly more helpful error message. + + (cherry picked from commit e3211eec0469dc6717943010d584207b7abdb1e0) + + Conflicts: + fml/src/main/java/cpw/mods/fml/client/SplashProgress.java + cpw: + Finish loading screen before going fullscreen. Closes MinecraftForge/FML#662 + + (cherry picked from commit 19d7e16fa6a28c5665de1ed6e50d8699e865bff2) + + Conflicts: + fml/patches/minecraft/net/minecraft/client/Minecraft.java.patch + +Build 1.8-11.14.2.1421: + cpw: + MinecraftForge/FML@12ccf9cf49b76140841cdc5a459422ae4781de1a Rather than try to fall back to the backup level.dat in case of weirdness in the ID map, just fail hard. There is probably a serious modder derp in this case and there's nothing FML can do to recover the situation except avoid making it worse. + MinecraftForge/FML@e8cd368da30661ed2898fff232e2db787edcbdcc It's an IllegalState not an IllegalArgument *sigh* + MinecraftForge/FML@2be9c743424c92f8799a6af1d59d60edd65e6bf0 And fix itemblocks being removed, leaving behind a residual block. This is a legitimate action - use the missing mapping event to let the mod tell us about it + MinecraftForge/FML@7d8804cf656081d1570068f52e9bfc7140b21a65 You can't setAction to BLOCKONLY + MinecraftForge/FML@c73861efe67594ee9995bc93744cab06bd6647d1 Cherry-pick 01aaa7dc97480b381ca0d192ec65016d7baeb747 Fix mods defined via --mods or --modListFile launch args not being searched for coremods. #560 + MinecraftForge/FML@94c45b48c1265e7c4f60f591d413fa545787d354 Fix json cache crash, handle the file much more cleanly. Probably an MC JIRA since it's a vanilla bug. Closes #619 + cpw: + Forge really should have always supported the concept of an "exact spawn". Individual world providers can still + change this behaviour of course, but for default maps it makes sense to support it as a config option. + cpw: + Removed fluidID from ItemStack. + + This fixes a rather huge issue where FluidStacks on the client could be desynced if a modder was unaware of it. + + This is a breaking change but can be mitigated with a transformer to the getter getFluidID(). + + Signed-off-by: King Lemming + cpw: Add in a fluidid transformer + cpw: Fix formatting + cpw: + Allow for duplicate Fluid Blocks. It's annoying to be sure but just as with the OreDictionary, we'll have to handle it. + + Signed-off-by: King Lemming + cpw: + You shouldn't be creating FluidStacks from unregistered Fluids. Warn clearly on failed registrations, and make a useful log message for failed fluidstack + creations. Should help a lot with tracking down broken mods that are doing this wrongly. + LexManos: MinecraftForge/FML@31cf2a9cab6d1977d31436220d9612eaa13d4e0f Remove J7 only constructor in EnhancedRuntimeException, J6 compiling compatibility restored. + LexManos: Restore binary compatibility issues in FluidRegistry caused by recent changes. + me: + Fix FluidRegsitry.registerFluid + + FluidRegistry: + ```java + static BiMap fluids = HashBiMap.create(); + static BiMap fluidIDs = HashBiMap.create(); + ... + public static boolean registerFluid(Fluid fluid) + { + if (fluidIDs.containsKey(fluid.getName())) + ^^^^^^^ + ``` + There is definitely should be fluids instead fluidIDs. This mistake broke many mods. + kinglemming: + Fixes #1782 + + Signed-off-by: King Lemming + kinglemming: + Fixes FluidContainerRegistry properly - no more corner case where client/server mods disagree. + + Also clarifies the 2x Fluid registration error message somewhat. + + Signed-off-by: King Lemming + kinglemming: + Adds a warning to the FluidContainerRegistry when a mod does something stupid! Also denies the registration. + + Signed-off-by: King Lemming + cpw: + MinecraftForge/FML@ce791cb1f2cf983ef77b1e5c4028ddefab394062 Rework EventSubscriptionTransformer to bake @Cancelable and @HasResult values, should increase EventBus performance even more by removing logic from Event constructors. + MinecraftForge/FML@852710962a9b6d7c8e2ca188c715eebb2da44c2a Clean up some dead code + cpw: MinecraftForge/FML@be5ec06e3144d55a03d125f3ce364eade3771f4f Cleaning up the missing resource stack spam, and condensing the information into a usefully understandable format. + cpw: MinecraftForge/FML@0da1263ff9ede99267c03728a1c823b8056d5e44 Enhance error output for bad textures a bit more and try and capture more types of error.. + cpw: + Fluids are now tracked internally by mod. This allows for the server and the world to specify a "default" + in the case of a possible alternative fluid implementation. If you always called registerFluid, things + should work pretty seamlessly, but if you didn't (gating with an isFluidRegistered check for example) + you should change to register anyway. This way, even if you're not default in the overall instance, you may + become default if you're the only mod present on a server, for example, or in a world save. + + This should radically decrease the mixups caused by mod load ordering problems, and other issues around fluid + tracking. + cpw: Fix NPE - Closes #1794 + cpw: And handle the null case in the constructor itself. Closes #1794 (again) + cpw: + MinecraftForge/FML@dfce4cd8d023a546c4c21405db182b8ddcd38633 Allow registering custom language adapters. + MinecraftForge/FML@9fecd72cf0bd483ae7bc2ce821ae6b2f5e5b9c65 Some tidyup of the PR + MinecraftForge/FML@10ac2a4fd972e923a60d23d10e8f297b8584f565 Fix itemCtorArgs javadoc in registerBlock + MinecraftForge/FML@86f70d37a40bbeaf7c389a14adcd8311ba5584df Patch TracingPrintStream to handle Kotlin IO. + kinglemming: + Adjusts OreDictionary to prevent invalid registrations. + Getting Ore Names for a non-existent ore will no longer automatically add that Name to the list nor generate an ID. + + Tweaks a warning message in the FluidContainerRegistry. No functionality change. + + Signed-off-by: King Lemming + LexManos: + Revert KL's change, requesting a ore WILL register it. + + Registering like this and returning a new empty list allows for modders to register their recipes and such without requiring to be executed after someone actually adds an item. If handled properly this allows for more flexible load orders, and more responsive code. + kinglemming: + Adds some new Ore querying functionality. + + Also attempts to size initial Hashmaps in a logical manner. + + Signed-off-by: King Lemming + cpw: + Fixed NBTSizeTracker missing a lot of data being read. Also made new NBT object allocation claim 32-bits in the size tracker. + + (cherry picked from commit de066a86da281d381b0e3ab9e83682720327049c) + + Conflicts: + patches/minecraft/net/minecraft/nbt/CompressedStreamTools.java.patch + patches/minecraft/net/minecraft/nbt/NBTTagList.java.patch + cpw: Verify that the fluid registry doesn't contain "junk" fluids thru reflection. + cpw: MinecraftForge/FML@8f9e3a7e30c8cc436dcb8d94b18b4634e0376339 Exceptions during construction phase should not propogate and cause an immediate crash. Closes #638 + cpw: Change logging to avoid the String.format bug. Closes #1809 + cpw: Actually use the override constraint in ticket requests. Closes #1802 + cpw: + Reformat ItemArmor$ArmorMaterial.getBaseItem to use an if/then/else structure. The switch + causes the generation of an internal class which may derp custom armor addition. + Closes #1799 + cpw: Make FluidStack hold a delegate for the fluid. This can then float based on what is "live" at present. + cpw: Fix static initializer derp. + cpw: Replace the OLD fluid in the fluidID map, not the new one.. + cpw: + Expose the fluid that a block was constructed with, useful for 'non-default' + configuration of fluidblocks. Shouldn't be used outside of this purpose. + cpw: MinecraftForge/FML@d14d1a8fea4c9242c944079ab8e4cdd516dfce4c Update to use the inherited jar format. Simplifies a lot.. + cpw: + MinecraftForge/FML@a39482c4b7ac2883f821619b47ff31e0b6e74b29 Splash screen implementation + MinecraftForge/FML@01fea095cdcd80c2ae9f0ebfd1c72242b3f2dbf8 Merge branch '1.7.10-load-progress' of github.com:RainWarrior/FML into 1.7.10 + MinecraftForge/FML@364b4bbbb0d4d168f9a63fa62a09e4e2fa213039 Call some loader stuffs + MinecraftForge/FML@61a891280d15f9f17e28bf86a427f32de5a8983e Make sure to close the splash screen if there's gonna be an error display + fry: + MinecraftForge/FML@5785a9c9e8d76b91a03ed1f9791aeee1cb7ea00b Fix up multiple injections of cmdline files via versionspecificmoddir. Closes #645 + MinecraftForge/FML@bd117be9c3e3919f3c29538cde80e3eb8fa48368 Correctly track exceptions thrown in the loading screen rendering thread + cpw: + MinecraftForge/FML@36688e781aae67fb1e4e7047acf689edeeac7ddb Add in resource reloading to the bar. Tidy up some of the labels a bit. + MinecraftForge/FML@a1dc465a55612ecdd44e6cde3adc0f1d53c6d97b More progress bar action! + MinecraftForge/FML@bfcbf4ef4366fd3d8bfd20adafb63a857bb0dd53 More progress bar hooks + MinecraftForge/FML@a6670c415ee97e771020921e00773c4c15e7512e Thread errors should be correctly displayed in the crash report now + MinecraftForge/FML@9a16d26186d27029cae32a19c09ddf48f7cba22e fixed bar text positioning + MinecraftForge/FML@0059c630281b7105c0532d2dba1bec27cf0323b2 Track mipmaps and texture upload + MinecraftForge/FML@ef5f809752e87e369235e98a63027e9347185cd9 Fix broken log message in vanilla. + cpw: + MinecraftForge/FML@4fe7b469b5ba156d4a786cd9e105b18cca7c271a Loading screen: logo rotation is now optional; initial support for animated textures - animation rate is fixed for now. + MinecraftForge/FML@31ae43590a2ba771d69b6c6513bcd5fe87ae8f8f Fix trying to close the screen during error. + cpw: MinecraftForge/FML@94821fac98e64d9b8ad7434ed23a621850a8e11c Add a config file that lets you add additional soft dependencies at runtime - injectedDependencies.json + cpw: MinecraftForge/FML@2ed00c4da0ee76eb15e28eb8ee2c07a3096155c2 Splash progress screen will not load in the presence of optifine anymore. + cpw: MinecraftForge/FML@dda431353953457608c38aacb060ef82ddc88883 Revert "Merge pull request #650 from luacs1998/1.7.10" This undoes the seriously broken change from Sponge to support Mixins, that breaks a wide variety of coremods. Given the widespread incompatibility it introduces, it won't be re-added at 1.7.10. + fry: + Updated FML: + MinecraftForge/FML@2ed00c4da0ee76eb15e28eb8ee2c07a3096155c2 Splash progress screen will not load in the presence of optifine anymore. + MinecraftForge/FML@adcf2247c69f68415033a3c0b2c527053733514c Loading screen: moved config file to the standard config directory; added the option to load textures from the custom resource pack + MinecraftForge/FML@91338433fa74e782e237643632de2cc5e17ee280 Add classloader exclusion for ASM + MinecraftForge/FML@7c10b93a2ded2799d41b73b67a2766c31e992d8a Synchronize the available libraries. Turns out we've been forcing a newer apache commons-lang(3.2.1) for a long time, also sync the dev guava - we've been forcing 17 for a long time too. Bumping commons-lang to 3.3.2 since that's what Mojang are shipping with 1.8. It has no observable ill effects Closes MinecraftForge/FML#651 + MinecraftForge/FML@8ccfa24764a3f4854f5334c0da1224286175e13b Merge pull request MinecraftForge/FML#650 from luacs1998/1.7.10 + MinecraftForge/FML@b2650a0bdb7d69010a55de518e76591a6c417e87 Optifine can tell us when they're ready for the new splash screen. + MinecraftForge/FML@02a5a58a1cbb25cd3baecf1535950e4780b7810f Fix the ordering of the messages, so they make sense now. + MinecraftForge/FML@dda431353953457608c38aacb060ef82ddc88883 Revert "Merge pull request MinecraftForge/FML#650 from luacs1998/1.7.10" This undoes the seriously broken change from Sponge to support Mixins, that breaks a wide variety of coremods. Given the widespread incompatibility it introduces, it won't be re-added at 1.7.10. + MinecraftForge/FML@5dbb481732bf4bcf8b0c5c02806051a933e6587e Eliminated texture name allocation race condition + MinecraftForge/FML@450b82ca0e13cf889a42eeb198b67115a4851031 Updated default forge logo to animated gif; reverted config folder resolution to Minecraft class due to Loader not being initialized at the point it's needed + cpw: MinecraftForge/FML@0b84b6aa297bdf6ab9f010e340f286442cb242dc Expose the state of the loader + cpw: + Attempt to fix the slow loading problem. Instead of forcing the main thread to wait around + on every call to processWindowMessages, we will simply skip it, if the mutex is already + claimed by the display thread. This should fix slow loading issues seen by some with + the new loading screen. + cpw: Hardcode the FML version in-game as a specific value. + cpw: Strip control codes in progress bar messages. They cause crashes sometimes. + cpw: + Clean up transformers a bit. Can't use COMPUTE_FRAMES even though it's required - the game refuses to even + run if I do. Note for j8: when we force Java8 classes, all coremods will need a thorough overhaul - the + current way we do things is not sustainable when Java8 becomes the universal norm. + cpw: + Add in an ItemStackHolder - a way to inject ItemStacks without having to have complex lookup code everywhere. + + Example: https://gist.github.com/cpw/9af398451a20459ac263 + cpw: Bump minor version # to 2 because of the fluid changes (mirroring what happening in 1.7.10) + +Build 1.7.10-10.13.3.1420-1.7.10: + cpw: + Add in an ItemStackHolder - a way to inject ItemStacks without having to have complex lookup code everywhere. + + Example: https://gist.github.com/cpw/9af398451a20459ac263 + +Build 1.8-11.14.1.1419: + LexManos: Fixed damage reduction rate of vanilla armor incorrectly scaling with armor's current durability. + +Build 1.8-11.14.1.1418: + AlgorithmX2: Fix Partial Face Lighting on Top/Bottom Faces ; Vanilla MC-80148 + +Build 1.8-11.14.1.1417: + LexManos: Force netty downgrade on dedicated server to match client. Netty bug: https://github.com/netty/netty/issues/2302 Closes #1848 + +Build 1.8-11.14.1.1416: + LexManos: Fixed command exploit with C12 + +Build 1.8-11.14.1.1415: + LexManos: Fixes MC-75630 - Exploit with signs and command blocks + +Build 1.8-11.14.1.1414: + LexManos: Finish loading screen before going fullscreen. Closes MinecraftForge/FML#662 + +Build 1.8-11.14.1.1413: + LexManos: Loosen up ServerChatEvent to support IChatComponent Closes #1893 + LexManos: Cleanup a lot of spammy output. Everything hidden behind environment flags now. Scale anvil image down 50% + LexManos: Attempt to disable the new splash screen if there are errors detected finishing up. Print a slightly more helpful error message. + +Build 1.8-11.14.1.1412: + me: Add true support for unicode fonts + +Build 1.8-11.14.1.1411: + diesieben07: Fix crash when texture loading throws RuntimeException without message + +Build 1.8-11.14.1.1410: + xxt1g3lxx.xxt1g3lxx: Changed forge command tab completion to use getListOfStringsMatchingLastWord() + +Build 1.8-11.14.1.1409: + xxt1g3lxx.xxt1g3lxx: Added tab completion + +Build 1.7.10-10.13.3.1408-1.7.10: + cpw: Strip control codes in progress bar messages. They cause crashes sometimes. + cpw: + Clean up transformers a bit. Can't use COMPUTE_FRAMES even though it's required - the game refuses to even + run if I do. Note for j8: when we force Java8 classes, all coremods will need a thorough overhaul - the + current way we do things is not sustainable when Java8 becomes the universal norm. + +Build 1.7.10-10.13.3.1407-1.7.10: + cpw: Hardcode the FML version in-game as a specific value. + +Build 1.7.10-10.13.3.1406-1.7.10: + cpw: Merged FML into Forge's repo. FML is no longer developed seperatly. + +Build 1.8-11.14.1.1405: + cpw: Merged FML into Forge's repo. FML is no longer developed seperatly. + +Build 1.8-11.14.1.1404: + tmtravlrsmail: + Initialized the Nether Fortress chest loot + + Forced the nether fortress chest loot to initialize in ChestGenHooks + like the other types. + + Changed tabs to spaces. Silly Eclipse. + +Build 1.7.10-10.13.3.1403-1.7.10: + fry: + Updated FML: + MinecraftForge/FML@2ed00c4da0ee76eb15e28eb8ee2c07a3096155c2 Splash progress screen will not load in the presence of optifine anymore. + MinecraftForge/FML@adcf2247c69f68415033a3c0b2c527053733514c Loading screen: moved config file to the standard config directory; added the option to load textures from the custom resource pack + MinecraftForge/FML@91338433fa74e782e237643632de2cc5e17ee280 Add classloader exclusion for ASM + MinecraftForge/FML@7c10b93a2ded2799d41b73b67a2766c31e992d8a Synchronize the available libraries. Turns out we've been forcing a newer apache commons-lang(3.2.1) for a long time, also sync the dev guava - we've been forcing 17 for a long time too. Bumping commons-lang to 3.3.2 since that's what Mojang are shipping with 1.8. It has no observable ill effects Closes MinecraftForge/FML#651 + MinecraftForge/FML@8ccfa24764a3f4854f5334c0da1224286175e13b Merge pull request MinecraftForge/FML#650 from luacs1998/1.7.10 + MinecraftForge/FML@b2650a0bdb7d69010a55de518e76591a6c417e87 Optifine can tell us when they're ready for the new splash screen. + MinecraftForge/FML@02a5a58a1cbb25cd3baecf1535950e4780b7810f Fix the ordering of the messages, so they make sense now. + MinecraftForge/FML@dda431353953457608c38aacb060ef82ddc88883 Revert "Merge pull request MinecraftForge/FML#650 from luacs1998/1.7.10" This undoes the seriously broken change from Sponge to support Mixins, that breaks a wide variety of coremods. Given the widespread incompatibility it introduces, it won't be re-added at 1.7.10. + MinecraftForge/FML@5dbb481732bf4bcf8b0c5c02806051a933e6587e Eliminated texture name allocation race condition + MinecraftForge/FML@450b82ca0e13cf889a42eeb198b67115a4851031 Updated default forge logo to animated gif; reverted config folder resolution to Minecraft class due to Loader not being initialized at the point it's needed + cpw: MinecraftForge/FML@0b84b6aa297bdf6ab9f010e340f286442cb242dc Expose the state of the loader + +Build 1.8-11.14.1.1402: + silfadur: Added hook for IPerspectiveAwareModel in RenderItem.renderItemIntoGUI for ItemCameraTransforms.TransformType.GUI + +Build 1.7.10-10.13.3.1401-1710ls: + fry: + Updated FML: + MinecraftForge/FML@2ed00c4da0ee76eb15e28eb8ee2c07a3096155c2 Splash progress screen will not load in the presence of optifine anymore. + MinecraftForge/FML@adcf2247c69f68415033a3c0b2c527053733514c Loading screen: moved config file to the standard config directory; added the option to load textures from the custom resource pack + MinecraftForge/FML@91338433fa74e782e237643632de2cc5e17ee280 Add classloader exclusion for ASM + MinecraftForge/FML@7c10b93a2ded2799d41b73b67a2766c31e992d8a Synchronize the available libraries. Turns out we've been forcing a newer apache commons-lang(3.2.1) for a long time, also sync the dev guava - we've been forcing 17 for a long time too. Bumping commons-lang to 3.3.2 since that's what Mojang are shipping with 1.8. It has no observable ill effects Closes MinecraftForge/FML#651 + MinecraftForge/FML@8ccfa24764a3f4854f5334c0da1224286175e13b Merge pull request MinecraftForge/FML#650 from luacs1998/1.7.10 + MinecraftForge/FML@b2650a0bdb7d69010a55de518e76591a6c417e87 Optifine can tell us when they're ready for the new splash screen. + MinecraftForge/FML@02a5a58a1cbb25cd3baecf1535950e4780b7810f Fix the ordering of the messages, so they make sense now. + MinecraftForge/FML@dda431353953457608c38aacb060ef82ddc88883 Revert "Merge pull request MinecraftForge/FML#650 from luacs1998/1.7.10" This undoes the seriously broken change from Sponge to support Mixins, that breaks a wide variety of coremods. Given the widespread incompatibility it introduces, it won't be re-added at 1.7.10. + MinecraftForge/FML@5dbb481732bf4bcf8b0c5c02806051a933e6587e Eliminated texture name allocation race condition + MinecraftForge/FML@450b82ca0e13cf889a42eeb198b67115a4851031 Updated default forge logo to animated gif; reverted config folder resolution to Minecraft class due to Loader not being initialized at the point it's needed + +Build 1.7.10-10.13.3.1400-1.7.10: + cpw: MinecraftForge/FML@dda431353953457608c38aacb060ef82ddc88883 Revert "Merge pull request #650 from luacs1998/1.7.10" This undoes the seriously broken change from Sponge to support Mixins, that breaks a wide variety of coremods. Given the widespread incompatibility it introduces, it won't be re-added at 1.7.10. + +Build 1.7.10-10.13.3.1399-1.7.10: + cpw: + MinecraftForge/FML@a39482c4b7ac2883f821619b47ff31e0b6e74b29 Splash screen implementation + MinecraftForge/FML@01fea095cdcd80c2ae9f0ebfd1c72242b3f2dbf8 Merge branch '1.7.10-load-progress' of github.com:RainWarrior/FML into 1.7.10 + MinecraftForge/FML@364b4bbbb0d4d168f9a63fa62a09e4e2fa213039 Call some loader stuffs + MinecraftForge/FML@61a891280d15f9f17e28bf86a427f32de5a8983e Make sure to close the splash screen if there's gonna be an error display + fry: + MinecraftForge/FML@5785a9c9e8d76b91a03ed1f9791aeee1cb7ea00b Fix up multiple injections of cmdline files via versionspecificmoddir. Closes #645 + MinecraftForge/FML@bd117be9c3e3919f3c29538cde80e3eb8fa48368 Correctly track exceptions thrown in the loading screen rendering thread + cpw: + MinecraftForge/FML@36688e781aae67fb1e4e7047acf689edeeac7ddb Add in resource reloading to the bar. Tidy up some of the labels a bit. + MinecraftForge/FML@a1dc465a55612ecdd44e6cde3adc0f1d53c6d97b More progress bar action! + MinecraftForge/FML@bfcbf4ef4366fd3d8bfd20adafb63a857bb0dd53 More progress bar hooks + MinecraftForge/FML@a6670c415ee97e771020921e00773c4c15e7512e Thread errors should be correctly displayed in the crash report now + MinecraftForge/FML@9a16d26186d27029cae32a19c09ddf48f7cba22e fixed bar text positioning + MinecraftForge/FML@0059c630281b7105c0532d2dba1bec27cf0323b2 Track mipmaps and texture upload + MinecraftForge/FML@ef5f809752e87e369235e98a63027e9347185cd9 Fix broken log message in vanilla. + cpw: + MinecraftForge/FML@4fe7b469b5ba156d4a786cd9e105b18cca7c271a Loading screen: logo rotation is now optional; initial support for animated textures - animation rate is fixed for now. + MinecraftForge/FML@31ae43590a2ba771d69b6c6513bcd5fe87ae8f8f Fix trying to close the screen during error. + cpw: MinecraftForge/FML@94821fac98e64d9b8ad7434ed23a621850a8e11c Add a config file that lets you add additional soft dependencies at runtime - injectedDependencies.json + cpw: MinecraftForge/FML@2ed00c4da0ee76eb15e28eb8ee2c07a3096155c2 Splash progress screen will not load in the presence of optifine anymore. + +Build 1.8-11.14.1.1398: + Zaggy1024: Fixed a bug which caused the light level not to update when a block implements Block.getLightValue(IBlockAccess, BlockPos) to change the light value for different block states. + +Build 1.8-11.14.1.1397: + Parker Young: Fixes MC-52974: Host's skin doesn't load in LAN + +Build 1.8-11.14.1.1396: + Zaggy1024: Removed @SideOnly(Side.CLIENT) from EnumWorldBlockLayer. + +Build 1.7.10-10.13.3.1395-1710ls: + cpw: MinecraftForge/FML@94821fac98e64d9b8ad7434ed23a621850a8e11c Add a config file that lets you add additional soft dependencies at runtime - injectedDependencies.json + +Build 1.7.10-10.13.3.1394-1710ls: + cpw: + MinecraftForge/FML@4fe7b469b5ba156d4a786cd9e105b18cca7c271a Loading screen: logo rotation is now optional; initial support for animated textures - animation rate is fixed for now. + MinecraftForge/FML@31ae43590a2ba771d69b6c6513bcd5fe87ae8f8f Fix trying to close the screen during error. + +Build 1.7.10-10.13.3.1393-1710ls: + cpw: + MinecraftForge/FML@36688e781aae67fb1e4e7047acf689edeeac7ddb Add in resource reloading to the bar. Tidy up some of the labels a bit. + MinecraftForge/FML@a1dc465a55612ecdd44e6cde3adc0f1d53c6d97b More progress bar action! + MinecraftForge/FML@bfcbf4ef4366fd3d8bfd20adafb63a857bb0dd53 More progress bar hooks + MinecraftForge/FML@a6670c415ee97e771020921e00773c4c15e7512e Thread errors should be correctly displayed in the crash report now + MinecraftForge/FML@9a16d26186d27029cae32a19c09ddf48f7cba22e fixed bar text positioning + MinecraftForge/FML@0059c630281b7105c0532d2dba1bec27cf0323b2 Track mipmaps and texture upload + MinecraftForge/FML@ef5f809752e87e369235e98a63027e9347185cd9 Fix broken log message in vanilla. + +Build 1.8-11.14.1.1392: + LexManos: Cleanup RenderEntityItem patch, fixes Z-fighting issue in EntityItems. Closes #1824 + +Build 1.7.10-10.13.3.1391-1710ls: + fry: + MinecraftForge/FML@5785a9c9e8d76b91a03ed1f9791aeee1cb7ea00b Fix up multiple injections of cmdline files via versionspecificmoddir. Closes #645 + MinecraftForge/FML@bd117be9c3e3919f3c29538cde80e3eb8fa48368 Correctly track exceptions thrown in the loading screen rendering thread + +Build 1.8-11.14.1.1390: + jadran.kotnik: + Extracted the creation of RegionRenderCache into a method. + Classes extending RegionRenderCache can change the behavior of the cache, allowing to visually change blocks (schematics etc). + +Build 1.7.10-10.13.3.1388-1.7.10: + cpw: MinecraftForge/FML@d14d1a8fea4c9242c944079ab8e4cdd516dfce4c Update to use the inherited jar format. Simplifies a lot.. + +Build 1.7.10-10.13.3.1385-1.7.10: + cpw: + Expose the fluid that a block was constructed with, useful for 'non-default' + configuration of fluidblocks. Shouldn't be used outside of this purpose. + +Build 1.7.10-10.13.3.1384-1.7.10: + cpw: Replace the OLD fluid in the fluidID map, not the new one.. + +Build 1.7.10-10.13.3.1383-1.7.10: + cpw: Fix static initializer derp. + +Build 1.7.10-10.13.3.1382-1.7.10: + cpw: Make FluidStack hold a delegate for the fluid. This can then float based on what is "live" at present. + +Build 1.7.10-10.13.3.1381-1.7.10: + cpw: + Reformat ItemArmor$ArmorMaterial.getBaseItem to use an if/then/else structure. The switch + causes the generation of an internal class which may derp custom armor addition. + Closes #1799 + +Build 1.7.10-10.13.3.1380-1.7.10: + cpw: Actually use the override constraint in ticket requests. Closes #1802 + +Build 1.7.10-10.13.3.1379-1.7.10: + cpw: Change logging to avoid the String.format bug. Closes #1809 + +Build 1.7.10-10.13.3.1378-1.7.10: + cpw: MinecraftForge/FML@8f9e3a7e30c8cc436dcb8d94b18b4634e0376339 Exceptions during construction phase should not propogate and cause an immediate crash. Closes #638 + +Build 1.7.10-10.13.3.1377-1.7.10: + cpw: Verify that the fluid registry doesn't contain "junk" fluids thru reflection. + +Build 1.7.10-10.13.3.1376-1.7.10: + cpw: + Fixed NBTSizeTracker missing a lot of data being read. Also made new NBT object allocation claim 32-bits in the size tracker. + + (cherry picked from commit de066a86da281d381b0e3ab9e83682720327049c) + + Conflicts: + patches/minecraft/net/minecraft/nbt/CompressedStreamTools.java.patch + patches/minecraft/net/minecraft/nbt/NBTTagList.java.patch + +Build 1.8-11.14.1.1375: + LexManos: Fixed NBTSizeTracker missing a lot of data being read. Also made new NBT object allocation claim 32-bits in the size tracker. + +Build 1.7.10-10.13.3.1374-1.7.10: + kinglemming: + Adds some new Ore querying functionality. + + Also attempts to size initial Hashmaps in a logical manner. + + Signed-off-by: King Lemming + +Build 1.7.10-10.13.3.1373-1.7.10: + LexManos: + Revert KL's change, requesting a ore WILL register it. + + Registering like this and returning a new empty list allows for modders to register their recipes and such without requiring to be executed after someone actually adds an item. If handled properly this allows for more flexible load orders, and more responsive code. + +Build 1.7.10-10.13.3.1372-1.7.10: + kinglemming: + Adjusts OreDictionary to prevent invalid registrations. + Getting Ore Names for a non-existent ore will no longer automatically add that Name to the list nor generate an ID. + + Tweaks a warning message in the FluidContainerRegistry. No functionality change. + + Signed-off-by: King Lemming + +Build 1.8-11.14.1.1371: + cpw: + MinecraftForge/FML@888e489394e52abdfb349fbfbd7f8e153b5af124 Allow registering custom language adapters. + MinecraftForge/FML@906f94ca143f756f40404fde38af32b2481d0673 Some tidyup of the PR + MinecraftForge/FML@1c025f18433df868859022eea8e6d198444736de Patch TracingPrintStream to handle Kotlin IO. + +Build 1.7.10-10.13.3.1370-1.7.10: + cpw: + MinecraftForge/FML@dfce4cd8d023a546c4c21405db182b8ddcd38633 Allow registering custom language adapters. + MinecraftForge/FML@9fecd72cf0bd483ae7bc2ce821ae6b2f5e5b9c65 Some tidyup of the PR + MinecraftForge/FML@10ac2a4fd972e923a60d23d10e8f297b8584f565 Fix itemCtorArgs javadoc in registerBlock + MinecraftForge/FML@86f70d37a40bbeaf7c389a14adcd8311ba5584df Patch TracingPrintStream to handle Kotlin IO. + +Build 1.7.10-10.13.3.1369-1.7.10: + cpw: And handle the null case in the constructor itself. Closes #1794 (again) + +Build 1.7.10-10.13.3.1368-1.7.10: + cpw: Fix NPE - Closes #1794 + +Build 1.7.10-10.13.3.1367-1.7.10: + cpw: + Fluids are now tracked internally by mod. This allows for the server and the world to specify a "default" + in the case of a possible alternative fluid implementation. If you always called registerFluid, things + should work pretty seamlessly, but if you didn't (gating with an isFluidRegistered check for example) + you should change to register anyway. This way, even if you're not default in the overall instance, you may + become default if you're the only mod present on a server, for example, or in a world save. + + This should radically decrease the mixups caused by mod load ordering problems, and other issues around fluid + tracking. + +Build 1.7.10-10.13.3.1366-1.7.10: + cpw: MinecraftForge/FML@0da1263ff9ede99267c03728a1c823b8056d5e44 Enhance error output for bad textures a bit more and try and capture more types of error.. + +Build 1.7.10-10.13.3.1365-1.7.10: + cpw: MinecraftForge/FML@be5ec06e3144d55a03d125f3ce364eade3771f4f Cleaning up the missing resource stack spam, and condensing the information into a usefully understandable format. + +Build 1.7.10-10.13.3.1364-1.7.10: + cpw: + MinecraftForge/FML@ce791cb1f2cf983ef77b1e5c4028ddefab394062 Rework EventSubscriptionTransformer to bake @Cancelable and @HasResult values, should increase EventBus performance even more by removing logic from Event constructors. + MinecraftForge/FML@852710962a9b6d7c8e2ca188c715eebb2da44c2a Clean up some dead code + +Build 1.7.10-10.13.3.1363-1.7.10: + kinglemming: + Adds a warning to the FluidContainerRegistry when a mod does something stupid! Also denies the registration. + + Signed-off-by: King Lemming + +Build 1.7.10-10.13.3.1362-1.7.10: + kinglemming: + Fixes FluidContainerRegistry properly - no more corner case where client/server mods disagree. + + Also clarifies the 2x Fluid registration error message somewhat. + + Signed-off-by: King Lemming + +Build 1.8-11.14.1.1361: + LexManos: MinecraftForge/FML@4a753227adb805d29f3bf245c3f8427193c35544 Rework EventSubscriptionTransformer to bake @Cancelable and @HasResult values, should increase EventBus performance even more by removing logic from Event constructors. + +Build 1.7.10-10.13.3.1360-1.7.10: + kinglemming: + Fixes #1782 + + Signed-off-by: King Lemming + +Build 1.8-11.14.1.1359: + Abrar Syed: changed run configs to GradleStarts + +Build 1.7.10-10.13.3.1358-1.7.10: + me: + Fix FluidRegsitry.registerFluid + + FluidRegistry: + ```java + static BiMap fluids = HashBiMap.create(); + static BiMap fluidIDs = HashBiMap.create(); + ... + public static boolean registerFluid(Fluid fluid) + { + if (fluidIDs.containsKey(fluid.getName())) + ^^^^^^^ + ``` + There is definitely should be fluids instead fluidIDs. This mistake broke many mods. + +Build 1.8-11.14.1.1357: + LexManos: + Updated FML: + MinecraftForge/FML@c8160311d580f2dfccdf796a5243e16844787cb6 Stop IllegalFormatConversionException thrown if @Mod has flagged client-only or server-only + MinecraftForge/FML@9a894952afb526436649f608f7af5992b97f044c Merge pull request #627 from GotoLink/patch-1 + MinecraftForge/FML@40faac64520d1a197f08eaa9a0f850e7df43359a Remove J7 only constructor in EnhancedRuntimeException, J6 compiling compatibility restored. + +Build 1.7.10-10.13.3.1356-1.7.10: + LexManos: MinecraftForge/FML@31cf2a9cab6d1977d31436220d9612eaa13d4e0f Remove J7 only constructor in EnhancedRuntimeException, J6 compiling compatibility restored. + LexManos: Restore binary compatibility issues in FluidRegistry caused by recent changes. + +Build 1.7.10-10.13.3.1355-1.7.10: + cpw: + Removed fluidID from ItemStack. + + This fixes a rather huge issue where FluidStacks on the client could be desynced if a modder was unaware of it. + + This is a breaking change but can be mitigated with a transformer to the getter getFluidID(). + + Signed-off-by: King Lemming + cpw: Add in a fluidid transformer + cpw: Fix formatting + cpw: + Allow for duplicate Fluid Blocks. It's annoying to be sure but just as with the OreDictionary, we'll have to handle it. + + Signed-off-by: King Lemming + cpw: + You shouldn't be creating FluidStacks from unregistered Fluids. Warn clearly on failed registrations, and make a useful log message for failed fluidstack + creations. Should help a lot with tracking down broken mods that are doing this wrongly. + +Build 1.8-11.14.1.1354: + ohai.iChun: Readded but deprecated the old RenderPlayerEvent that were deleted. Sorry, Lex. + +Build 1.8-11.14.1.1353: + ohai.iChun: + Reimplement RenderPlayerEvent that was removed in the port to 1.8 from 1.7.10. + RenderPlayerEvent.Specials was removed because the special effects are done in the LayerRenderer now. + +Build 1.7.10-10.13.2.1352-1.7.10: + cpw: + Forge really should have always supported the concept of an "exact spawn". Individual world providers can still + change this behaviour of course, but for default maps it makes sense to support it as a config option. + +Build 1.7.10-10.13.2.1351-1.7.10: + cpw: + MinecraftForge/FML@12ccf9cf49b76140841cdc5a459422ae4781de1a Rather than try to fall back to the backup level.dat in case of weirdness in the ID map, just fail hard. There is probably a serious modder derp in this case and there's nothing FML can do to recover the situation except avoid making it worse. + MinecraftForge/FML@e8cd368da30661ed2898fff232e2db787edcbdcc It's an IllegalState not an IllegalArgument *sigh* + MinecraftForge/FML@2be9c743424c92f8799a6af1d59d60edd65e6bf0 And fix itemblocks being removed, leaving behind a residual block. This is a legitimate action - use the missing mapping event to let the mod tell us about it + MinecraftForge/FML@7d8804cf656081d1570068f52e9bfc7140b21a65 You can't setAction to BLOCKONLY + MinecraftForge/FML@c73861efe67594ee9995bc93744cab06bd6647d1 Cherry-pick 01aaa7dc97480b381ca0d192ec65016d7baeb747 Fix mods defined via --mods or --modListFile launch args not being searched for coremods. #560 + MinecraftForge/FML@94c45b48c1265e7c4f60f591d413fa545787d354 Fix json cache crash, handle the file much more cleanly. Probably an MC JIRA since it's a vanilla bug. Closes #619 + +Build 1.8-11.14.1.1350: + LexManos: + Updated FML: + MinecraftForge/FML@1de3bf733aef754f15de55006b1750376871feb0 Fix FML's package for net.miencraftforge on maven. Fixes uploading of new builds. Note: 1.7.10 builds are bug fixes only while FML/Forge for 1.8 stabelizes. + MinecraftForge/FML@05ecefb53857ecc1dc52d4a577ed593c26da9659 Update to ASM5 for Java 8 support + MinecraftForge/FML@1112c455b9758c38eab385f48578bad17c0180f9 Merge pull request #595 from Grinch/master + LexManos: Fixed potential NPEs in Structure code caused by componenets not being able to load. + cpw: + MinecraftForge/FML@6b0ae369eb9b8cf89eb9d53fe997a6e5ef222093 Try and improve performance of the registry by avoiding superType.cast. + MinecraftForge/FML@dfebcafd49550b8c3f90c6c028ef0d7f3a13e607 Something I meant to add a long time ago, but it got overlooked. My apologies. Presend the (int) dimension ID in the serverhello packet. This should be 100% backward compatible with existing servers but bumping a server to this version will allow clients with this version as well to now login in dimensions outside the byte range. Probably fixes a bunch of mods that add dimensions. + MinecraftForge/FML@6011419fa055c1375d05189f9bf0d86705c9c0ec Clean up my patch. Terrible person I am.. + MinecraftForge/FML@c6bbd0e82de3d8f7993d70aa7be3f883b3afbc47 Forgot that I need a noarg ctor. I'm so rusty. + MinecraftForge/FML@6edc1635de163c41b6b5dfe02bee13d6a9c5aa4d Need to load the dimension from disk - do it without filling out the player so that the normal player loading (including events) works properly + cpw: + MinecraftForge/FML@4ca6f6e19f3c3147fcf48c58669f55478a9a1345 What a dumb oversight. One needs to copy the active substitution set to the active gamedata. Doesn't work well otherwise. + MinecraftForge/FML@6f6cec96be73b4c94999cf60dc00741f3f3c2cc2 Substitutions need to be activated when they're registered, otherwise they won't appear in world, ever. + MinecraftForge/FML@13ac015f0c36b8e2091ae332c556be315429f4c8 Change iterator behaviour for the registry - include substitutions in the standard iterator, so that vanilla MC methods visit them (so they can get textures etc). The fml special one only visits the absolute set, used mostly for sanity checking and serialization. + LexManos: + MinecraftForge/FML@24cb4a42c4e4bddde95c0e49d1f8b8bcc20c626d Enhance output of common FML errors in crash reports and server GUI. + Force load anonymous minecraft classes used in crash reports. This prevents some crashes being hiddedn behind class definiton exceptions. + cpw: Experimental "fix" for the weird rendering in the Acheivements screen. Thanks skyboy for spotting this. + cpw: + Comment on previous commit fix - it worked. Thanks to skyboy and tterrag for investigating and verifying. + A chest as an acheivement icon will recreate the original problem, for note. + cpw: + Update FML - merge the 1.7.10 changes in to forge @ 1.8. Wow git is (sorta) AWESOME! + + MinecraftForge/FML@6b0ae369eb9b8cf89eb9d53fe997a6e5ef222093 Try and improve performance of the registry by avoiding superType.cast. + MinecraftForge/FML@dfebcafd49550b8c3f90c6c028ef0d7f3a13e607 Something I meant to add a long time ago, but it got overlooked. My apologies. Presend the (int) dimension ID in the serverhello packet. This should be 100% backward compatible with existing servers but bumping a server to this version will allow clients with this version as well to now login in dimensions outside the byte range. Probably fixes a bunch of mods that add dimensions. + MinecraftForge/FML@6011419fa055c1375d05189f9bf0d86705c9c0ec Clean up my patch. Terrible person I am.. + MinecraftForge/FML@c6bbd0e82de3d8f7993d70aa7be3f883b3afbc47 Forgot that I need a noarg ctor. I'm so rusty. + MinecraftForge/FML@6edc1635de163c41b6b5dfe02bee13d6a9c5aa4d Need to load the dimension from disk - do it without filling out the player so that the normal player loading (including events) works properly + MinecraftForge/FML@4ca6f6e19f3c3147fcf48c58669f55478a9a1345 What a dumb oversight. One needs to copy the active substitution set to the active gamedata. Doesn't work well otherwise. + MinecraftForge/FML@6f6cec96be73b4c94999cf60dc00741f3f3c2cc2 Substitutions need to be activated when they're registered, otherwise they won't appear in world, ever. + MinecraftForge/FML@13ac015f0c36b8e2091ae332c556be315429f4c8 Change iterator behaviour for the registry - include substitutions in the standard iterator, so that vanilla MC methods visit them (so they can get textures etc). The fml special one only visits the absolute set, used mostly for sanity checking and serialization. + MinecraftForge/FML@24cb4a42c4e4bddde95c0e49d1f8b8bcc20c626d Enhance output of common FML errors in crash reports and server GUI. + MinecraftForge/FML@12ccf9cf49b76140841cdc5a459422ae4781de1a Rather than try to fall back to the backup level.dat in case of weirdness in the ID map, just fail hard. There is probably a serious modder derp in this case and there's nothing FML can do to recover the situation except avoid making it worse. + MinecraftForge/FML@e8cd368da30661ed2898fff232e2db787edcbdcc It's an IllegalState not an IllegalArgument *sigh* + MinecraftForge/FML@2be9c743424c92f8799a6af1d59d60edd65e6bf0 And fix itemblocks being removed, leaving behind a residual block. This is a legitimate action - use the missing mapping event to let the mod tell us about it + MinecraftForge/FML@7d8804cf656081d1570068f52e9bfc7140b21a65 You can't setAction to BLOCKONLY + MinecraftForge/FML@13df640d9d4516219b07778edd76efd2643019f6 Pull in a lot of the FML tweaks from 1.7 to 1.8 + MinecraftForge/FML@447beaa99ec828fb83796185d07c72ea28b056c9 Merge remote-tracking branch 'origin/1.7.10' + +Build 1.8-11.14.1.1349: + cpw: + MinecraftForge/FML@2afd55ab825fad3b07073c474cdb96b348701084 Fix scala mods, Closes #621 + MinecraftForge/FML@c541b08ef68161f437eeb7b22eabe27b20eebf55 Merge pull request #622 from diesieben07/scala-fix + MinecraftForge/FML@d5021417dd10f36dc3d1b68e4975eb91f7f46e68 Forgot the register handling bit. Registration should work now.. + MinecraftForge/FML@5a65c6568699acaade8243040d8552b1f2e2e28f OK, this is finally actually working, I think... + +Build 1.8-11.14.1.1348: + fry: Fixed mipmapping not being enabled by expanding 1x1 texture + +Build 1.7.10-10.13.2.1347-1.7.10: + cpw: + Comment on previous commit fix - it worked. Thanks to skyboy and tterrag for investigating and verifying. + A chest as an acheivement icon will recreate the original problem, for note. + +Build 1.7.10-10.13.2.1346-1.7.10: + cpw: Experimental "fix" for the weird rendering in the Acheivements screen. Thanks skyboy for spotting this. + +Build 1.8-11.14.1.1344: + LexManos: + Updated FML: + MinecraftForge/FML@2a268cd5664b6562a4bf2a953a6a93fd8e111bd2 Improve mod list GUI, add sort and search + MinecraftForge/FML@951fc2d9fd7e7970c86accb1be095a24f7bfaf18 First attempt at making FMLControlledRegistry something a bit more generic than just blocks/items. + MinecraftForge/FML@410582222d9ba15d42dc47db0d3d6a84aeac2d22 Merge pull request #614 from tterrag1098/betterModList + MinecraftForge/FML@ba0b176430cdbc3573643a6e21d47013cfd1f0e0 Fix ModDiscoverer ignoring inner classes. + MinecraftForge/FML@9cc313eab9939724786f833f511a87c9957dbc72 Merge pull request #617 from diesieben07/innerclass-disc + MinecraftForge/FML@01aaa7dc97480b381ca0d192ec65016d7baeb747 Fix mods defined via --mods or --modListFile launch args not being searched for coremods. #560 + MinecraftForge/FML@8cecc47b85db68e8e69f45641b1d843509dbe71d Merge pull request #620 from killjoy1221/extra-coremod-fix + MinecraftForge/FML@38d9a5f444815810dec3607f5b3b7ff1ac513d4c Enhance output of common FML errors in crash reports and server + + Force load anonymous minecraft classes used in crash reports. This prevents some crashes being hiddedn behind class definiton exceptions.GUI. + +Build 1.7.10-10.13.2.1343-1.7.10: + LexManos: + MinecraftForge/FML@24cb4a42c4e4bddde95c0e49d1f8b8bcc20c626d Enhance output of common FML errors in crash reports and server GUI. + Force load anonymous minecraft classes used in crash reports. This prevents some crashes being hiddedn behind class definiton exceptions. + +Build 1.7.10-10.13.2.1342-1.7.10: + cpw: + MinecraftForge/FML@4ca6f6e19f3c3147fcf48c58669f55478a9a1345 What a dumb oversight. One needs to copy the active substitution set to the active gamedata. Doesn't work well otherwise. + MinecraftForge/FML@6f6cec96be73b4c94999cf60dc00741f3f3c2cc2 Substitutions need to be activated when they're registered, otherwise they won't appear in world, ever. + MinecraftForge/FML@13ac015f0c36b8e2091ae332c556be315429f4c8 Change iterator behaviour for the registry - include substitutions in the standard iterator, so that vanilla MC methods visit them (so they can get textures etc). The fml special one only visits the absolute set, used mostly for sanity checking and serialization. + +Build 1.8-11.14.1.1341: + simonbarnes1: + Add getTileData() to TileEntity + + - The same idea as Entity.getEntityData() + +Build 1.7.10-10.13.2.1340-1.7.10: + cpw: + MinecraftForge/FML@6b0ae369eb9b8cf89eb9d53fe997a6e5ef222093 Try and improve performance of the registry by avoiding superType.cast. + MinecraftForge/FML@dfebcafd49550b8c3f90c6c028ef0d7f3a13e607 Something I meant to add a long time ago, but it got overlooked. My apologies. Presend the (int) dimension ID in the serverhello packet. This should be 100% backward compatible with existing servers but bumping a server to this version will allow clients with this version as well to now login in dimensions outside the byte range. Probably fixes a bunch of mods that add dimensions. + MinecraftForge/FML@6011419fa055c1375d05189f9bf0d86705c9c0ec Clean up my patch. Terrible person I am.. + MinecraftForge/FML@c6bbd0e82de3d8f7993d70aa7be3f883b3afbc47 Forgot that I need a noarg ctor. I'm so rusty. + MinecraftForge/FML@6edc1635de163c41b6b5dfe02bee13d6a9c5aa4d Need to load the dimension from disk - do it without filling out the player so that the normal player loading (including events) works properly + +Build 1.8-11.14.1.1339: + erlend: Added newVolume and newPitch to PlaySoundAtEntityEvent. Deprecated ForgeEventFactory.onPlaySoundAt, added replacement ForgeEventFactory.onPlaySoundAtEntity. + +Build 1.8-11.14.1.1338: + Geforce132: -Added EntityMountEvent. + +Build 1.8-11.14.1.1337: + diesieben07: Fix jukebox message being too low with forge + +Build 1.8-11.14.1.1336: + LexManos: + Updated FML: + MinecraftForge/FML@3e7ae47f8f5d642b256adbe8b3395bb40daf85da Fix Event Bus Access Issues + MinecraftForge/FML@c8e2a5f377ddf8a35cceda6a14697dbe8cad4ca8 Fixed WorldInfo properties not loaded + MinecraftForge/FML@7f96b2c69ab8a2ed07b5b786b3d679ea4c509121 Fix Debug packet logger on local memory connections. + +Build 1.8-11.14.1.1335: + robin: remove translation and add a note for contributors + +Build 1.8-11.14.1.1334: + erju01: Fixed enchanting table applying secondary enchs. + +Build 1.8-11.14.1.1333: + mnmiller1: Add NeighborNotiftyEvent. + +Build 1.8-11.14.1.1332: + tmtravlrsmail: + Added Nether Fortress chest to the ChestGenHooks + + I tried to follow the directions on + + https://github.com/MinecraftForge/MinecraftForge/wiki/If-you-want-to-contribute-to-Forge + + as best as I could. =) + + If anything is wrong, let me know and I'll change it! + clienthax: + Current spawner implementation checks the EntitySpawnPlacementRegistry hashmap to check where a entity should spawn + as there is no way to modify this without the use of reflection or a AT, you can not specify where you want your entity to spawn + adding this helper method will allow developers to specify where they want their entity to spawn. + + Signed-off-by: Clienthax + + Update EntitySpawnPlacementRegistry.java.patch + LexManos: Fix compile error in ClientHax's PR. + +Build 1.8-11.14.1.1329: + fry: Hopefully fix NPE during baking of empty vanilla item models + +Build 1.8-11.14.1.1328: + LexManos: Fire WorldEvent.Load for Client worlds. Closes #1719 + +Build 1.8-11.14.1.1327: + LexManos: Make Container.mergeItemStack respect Slot.isValidItem Closes #1630 + +Build 1.8-11.14.1.1326: + LexManos: Fix JukeBoxes not storing the inserted record. All TE's in minecraft are in net.minecraft.tileentity EXCEPT JukeBoxes. Closes #1633 Closes #1714 + +Build 1.8-11.14.1.1325: + LexManos: Fix NPEs in last commit. + +Build 1.8-11.14.1.1324: + LexManos: + Updated FML: + MinecraftForge/FML@c9cf3136c265b2e8e46eab102b2310a9312b8cfb New @Mod properties to define which environment to load the mod on. + clientSideOnly will only be loaded in the Client environment. + serverSideOnly will only be loaded in the Dedicated server environment. + Combine with acceptedMinecraftVersions to prevent users from loading the mod in the incorrect environment. + +Build 1.8-11.14.1.1323: + LexManos: + Updated FML: + MinecraftForge/FML@5eff40897545c9e6f597a202bc9e86c3b07761ad Filter more known libraries from potential mod canidates. + MinecraftForge/FML@feb4c436db27a249dd5190023edd38cb5884e90b Quiet ClassPatchManager debug spam by default. Reenable using -Dfml.debugClassPatchManager=true. + MinecraftForge/FML@41e806fa950839bf901ebf9c18d0c632a7c5538c Fix double decoding of UTF8 characters in lang files. + +Build 1.8-11.14.1.1322: + LexManos: Fixed using items on the wrong block client side caused by iChuns eyeheight update. + +Build 1.8-11.14.1.1321: + ohai.iChun: Reimplement variable eyeHeight for players which was removed in the 1.7 to 1.8 port. + +Build 1.8-11.14.1.1320: + fry: Removed event bus call from the ModelLoader + +Build 1.8-11.14.1.1319: + matthewprenger: Add the ability to add prefixes and suffixes to the player's display name. + LexManos: Add safty to URL detection in chat. Closes #1712 + +Build 1.8-11.14.1.1318: + fry: Added a default white texture; Fixed B3DLoader crashing when the brush has empty texture specified + fry: RenderItem can now use baked quad color + +Build 1.8-11.14.1.1317: + Parker Young: + Added ATs for EnumFacing + + This publics the VALUES array and HORIZONTALS array in EnumFacing, thus giving modders access to these arrays, much like ForgeDirection had. + +Build 1.8-11.14.1.1316: + fry: Attributes.put (de)normalization now works as intended + +Build 1.8-11.14.1.1315: + LexManos: Hold a weak reference to the Minecraft fake player object. Closes #1705 + +Build 1.8-11.14.1.1314: + matthewprenger: Minor tweak to UsernameCache to also cache usernames of offline players. + +Build 1.8-11.14.1.1313: + fry: Updated FML + +Build 1.8-11.14.1.1312: + fry: ModelRotation.getMatrix() now returns the correct matrix; fixed the application of custom transformations to vanilla models; fixed application of transformations to B3D models; fixed the culling of rotated vanilla models + +Build 1.8-11.14.1.1311: + fry: Alternative models work once again + +Build 1.8-11.14.1.1310: + fry: Updated FML + +Build 1.8-11.14.1.1309: + fry: fix ModelLoader.setCustomModelResourceLocation not storing same item with different metadata values + +Build 1.8-11.14.1.1308: + LexManos: Fixed debugging Dedicated server in Forge Dev workspace. + LexManos: Fixed being kicked from the server while swimming. + fry: + fixed NPE during loading of B3D models without textures/brushes. + changed default B3D color to have full opacity. + fry: Reworked vanilla texture resolution, hopefully fixes NPE bug in FaceBakery + fry: made renderLayer ThreadLocal, now it should hold correct value for use inside custom baked models + LexManos: Try a maven mirrior to fix build issues. + +Build 1.7.10-10.13.2.1307-1.7.10: + LexManos: Fixed potential NPEs in Structure code caused by componenets not being able to load. + +Build 1.8-11.14.1.1306: + LexManos: Fixed potential NPEs in Structure code caused by componenets not being able to load. Closes #1686 + +Build 1.8-11.14.1.1305: + LexManos: Fixed InitMapGenEvent's fire order so values are used. And added OCEAN_MONUMENT type. Closes #1681 + LexManos: Fixed BlockBush and BlockCrops not respecting custom soils. Closes #1683 + +Build 1.8-11.14.1.1303: + fry: Restore binary backwards compatibility + +Build 1.8-11.14.1.1302: + fry: ModelBakeEvent now has ModelLoader as an argument instead of ModelBakery; Added various static hooks to ModelLoader to allow registering model-related information before it's needed (prevents file-not-found errors on first baking pass); ModelLoader waits until ModelBakeEvent is done before showing any missing model exceptions; It's now possible to define models completely in-code (as illustrated by ModelBakeEventDebug). + +Build 1.8-11.14.1.1301: + LexManos: Bump version to 11.14.1 for next development cycle. + +Build 1.8-11.14.0.1299: + fry: Fixed tracking of UV locking state. Closes #1679 + +Build 1.8-11.14.0.1298: + LexManos: Fixed ItemFrames not having a model. Closes #1678 + LexManos: Updated FML: MinecraftForge/FML@22c9656196dbbea8ed983663d536c3ca272d7282 Reset S->C CustomPayload data after Write, allowing the same packet to be written multiple times. + +Build 1.8-11.14.0.1297: + LexManos: Fixed domain support in ArmorMaterial texture name. Closes #1675 + LexManos: Fixed Buttons not dropping anything when broken. Closes #1676 + +Build 1.8-11.14.0.1296: + LexManos: + Initial update to 1.8, Super beta. Most rendering related hooks are out due to major changes in 1.8. + + Some notes: + Almost all int x, int y, int z parameters have been changed to BlockPos class + ForgeDirection has been removed, replaced by net.minecraft.util.EnumFacing. + All FML classes have moved from packet cpw.mods.fml to net.minecraftforge.fml + Fluid Rendering has been disabled for the time being, to be re-evaulated and a test mod created for it. + Minecraft now uses a Model based system for rendering blocks and Items. The intention is to expand the model format to better suit modder's needed once it is evaulated. + As such, The model loaders from Forge have been removed, to be replaced by expanding vanilla's model format. + Metadata has been extracted out in Minecraft to IBlockState, which holds a list of properties instead of magic number metadata. DO NOT listen to the fearmongering, you can do EVERYTHING with block states you could previously with metadata. + Stencil Bits are disabled entirely by for the main Display, Modders must enable and recreate the FrameBuffer if they wish to use Stencil Bits. + LexManos: Fix local variable conflict in Forge patch and latest MCP mappings. + LexManos: Fix fog colors, Closes #1524 + LexManos: Fix acedential inversion causing some tress to not have leaves. Closes #1522 + LexManos: Fix creative picking a CommandBlock minecart returning wrong item. Closes #1523 + LexManos: Fixed crash with caomparators due to wrong position. Closes #1512 + LexManos: Fix inverted logic preventing blocks from breaking. + LexManos: Fix Entityies not taking damage correctly. Closes #1511 + LexManos: Fix wrong state being passed to Block.getDrops + LexManos: Fix potential NPE in Block.isToolEffective + LexManos: Update RecipeSorter for new 1.8 recipies. + LexManos: Fix destroy particles not being added. Closes #1528 + LexManos: Fix debug screen not showing grey background. Closes #1529 + LexManos: Fix not being able to place blocks in liquids, and related issues. + LexManos: + Updated FML: + MinecraftForge/FML@36644e97714b46dbbb24416febdde1332a3e753c Finalize modded handshakes in the World tick thread. Prevents potential CMEs when login event takes to long to fire. + LexManos: Fix interacting with entities. + LexManos: Fixed Entity extended properties init order. Closes #1532 + LexManos: Fixed Entities not being able to climb ladders, Closes #1535 + LexManos: Fixed snow layers not being able to stack more then twice. Closes #1534 + LexManos: Fix BlockPane's connection detection. + LexManos: Fixed vanilla bug where top part of double plants would flicker a tifferent texture before dissapearing. + LexManos: Fix NPE with BlockSnapshots that caused items with TileEntities to be used up in creative mode. + LexManos: Fixed vanilla issue where exceptions in World tasks would not be logged. + LexManos: Fixed blocks not breaking properly when instantly destroied. + LexManos: Fix stickey pistons not retracting properly. + LexManos: + Updated FML: + MinecraftForge/FML@9c8ca4a4e3c4acc4980535e5c60da169b75a7810 Unlink banner block and item id. Mojang should of matched these up but they didn't -.- + MinecraftForge/FML@84a101f344b8fc21de1201fde717fbcbcba2aa79 Update Dev mcp mappings to 11-30 snapshot. + LexManos: Remove debug patch I left in. + LexManos: Fix finding of spawn location for mobs. Closes #1546 + LexManos: Only call blockBreak when block itself changes, not just meta. Fixes bottles poping out of brewing stands. + fry: Added model bake event (allows mods to insert custom baked models, much like TextureStitchEvent allows to load custom textures), ISmartBlock/ItemModel (ability form models to react to block/item states), Block.getExtendedState, support for unlisted properties in block states. Includes example implementation of http://imgur.com/a/FyyJX + LexManos: Added Explosion Start and Detonate events to control explosion. + LexManos: Fixed bug in ServerConfigurationManager.transferPlayerToDimension where it would send the old dimension's information. + LexManos: Add "sand" to the OreDictionary + LexManos: Cleanup deprecated code, and TODOs in OreDictionary. Down-typed things from ArrayList to List. Asking for the ores with a null stack will now throw an Exception. + LexManos: Added CreateSpawnPosition event. + LexManos: Add LivingHealEvent called from EntityLivingBase.heal() + LexManos: Added PotionBrewEvent.Pre/Post. To allow for modification and cancelation of Brewing. + LexManos: Add quartz_ore tool init, more mojang special casing -.- + LexManos: Changed ToolMaterial's repair material to ItemStack version to allow metadata sensitive versions. + LexManos: Improved Control of Enchantment.canApplyTogether() in Mod Enchantments, allowing both enchantments to determine if they can apply together. + LexManos: Expand PlayerWakupEvent to expose the three parameters passed into EntityPlayer.wakeUp. + LexManos: Unbind Shaped/Shapeless Ore Recipies from Array list to normal List. + LexManos: Fixed NPE thrown when brewing event is fired and not all slots are filled. Closes #1564 + LexManos: Make daylight sensor recipe use ore dictionary wooden slabs Closes #1565 + LexManos: Fix missed patch causing Dispensed Armor to go into the wrong slot. Closes #1560 + LexManos: Fixed log spam when breaking DoublePlants. Closes #1555 + LexManos: Fix texture stitcher not using all avalible spaces. + LexManos: Fix EnumHelper for new ArmorTexture argument. + LexManos: Untie ItemModelMesher from using Item Ids internally by implementing our own simple mechanics using Trove. + LexManos: Fix userdev for new BlockState change. + LexManos: MinecraftForge/FML@e3785c28930a218cf9374458c67c34e7fba17922 Ensure that EntitySpawn and OpenGUI packets are handled in the world thread. Also log all errors that are thrown in FutureTasks. + LexManos: Remove our changes to Stitcher slot allocation. + LexManos: Fix incorrect logic in world change clumping. + LexManos: MinecraftForge/FML@5a4d362293fe70e1421d1f22c4a195944731d6ba Finish removing marker in mapping entry names. FMLMissingMappingsEvent/FMLModIdMappingEvent should fire with correct names now. + jadran.kotnik: Fixed messages not being added to the chat history and ClientCommandHandler not being called when sleeping. + LexManos: Fix BlockSnapshots not firing correctly due to patch mixup in 1.8 update. Closes #1591 + palechip: Fix Scoreboard rendering for the sidebar. + LexManos: Removed exclusion of white stained glass recipes in ore dictionary. + LexManos: Added chests to the ore dictionary. + LexManos: Fix crafting of non-oak fences/gates. + LexManos: Fix MC-30864 (sending web links in chat) + LexManos: Fix recipies for stone variants. + Chicken-Bones: Allow blocks to render in multiple layers + lumien231: Fixes #1603: Moving the start of the update thread to the pre init of the forge mod container + LexManos: Fix potential NPE when loading a single player world where you were saved in a unloaded dimension. Closes #1575 + LexManos: Call World.init from DimensionManager.initDimension Closes #1551 + LexManos: Made Chunk.fillBlock respect TileEntity.shouldRefresh. + LexManos: Fix value passed for Item.getModel useRemaining argument. Closes #1623 + LexManos: Silently eat exceptions when getting a TE's rendering bounding box, this 'fixes' Bukkit servers screwing up world data and causing clients to crash. + jadran.kotnik: Fixed NPE when canceling ClientChatRecievedEvent. Fixes #1644 + Parker Young: Re-enabled Icon setting for Fluids + LexManos: + Updated FML: + MinecraftForge/FML@a55e4124531119f1c9c023cff74cfa09b49ef0e0 Save the mod list of players in their NetworkDispatcher (Make it accessable for mods) + MinecraftForge/FML@69d479d46ae658c5a5c2c00081be3df38e38c748 Fixed getEffectiveSide() for Netty Server threads + MinecraftForge/FML@0f9a33cf14165ddd424a7d82c2178cf5854bf32f Exclude only log4j2 queue from class loader + jadran.kotnik: Don't skip the first line when rendering (debug) text. + fry: + Added model loader registry + + Entry point: ModelLoaderRegistry + loader interface: ICustomModelLoader + custom model: IModel + + ModelLoader is responsible for splicing into vanilla model system. + (you probably don't need to use it directly) + + Interop with vanilla models isn't great yet + (vanilla models can't refer to custom ones as parents), will improve in + the future. + + Includes loader for B3D models, with animation support + (net.minecraftforge.client.model.b3d). + Blender export plugin with compatible coordinate system: + https://github.com/RainWarrior/B3DExport + + OBJ loader is being written, will be included at some point in the + future. For now you can convert OBJ to B3D via blender, or wait. + techStackLp: + Closes #1552 + + Set the destination block the to the fluid. + In the previous code the destination block was still Air and would would + cause a crash because air doesn't have a property for LEVEL + fry: Removed leftover debug messages + LexManos: + Updated FML: + MinecraftForge/FML@d00feb58c762b0bbc506d79faf1ce40bc96732e9 Remove debug code that was causing console spam in Forge. + MinecraftForge/FML@1de3bf733aef754f15de55006b1750376871feb0 Fix FML's package for net.miencraftforge on maven. Fixes uploading of new builds. Note: 1.7.10 builds are bug fixes only while FML/Forge for 1.8 stabelizes. + MinecraftForge/FML@05ecefb53857ecc1dc52d4a577ed593c26da9659 Update to ASM5 for Java 8 support + MinecraftForge/FML@1112c455b9758c38eab385f48578bad17c0180f9 Merge pull request #595 from Grinch/master + MinecraftForge/FML@9c3013e02af1bd2f724d34a30e0b880b6e131645 Merge remote-tracking branch 'origin/1.8' + +Build 1.8-11.14.0.1295-1.8: + fry: Removed leftover debug messages + +Build 1.8-11.14.0.1294-1.8: + techStackLp: + Closes #1552 + + Set the destination block the to the fluid. + In the previous code the destination block was still Air and would would + cause a crash because air doesn't have a property for LEVEL + +Build 1.8-11.14.0.1293-1.8: + jadran.kotnik: Don't skip the first line when rendering (debug) text. + +Build 1.8-11.14.0.1292-1.8: + fry: + Added model loader registry + + Entry point: ModelLoaderRegistry + loader interface: ICustomModelLoader + custom model: IModel + + ModelLoader is responsible for splicing into vanilla model system. + (you probably don't need to use it directly) + + Interop with vanilla models isn't great yet + (vanilla models can't refer to custom ones as parents), will improve in + the future. + + Includes loader for B3D models, with animation support + (net.minecraftforge.client.model.b3d). + Blender export plugin with compatible coordinate system: + https://github.com/RainWarrior/B3DExport + + OBJ loader is being written, will be included at some point in the + future. For now you can convert OBJ to B3D via blender, or wait. + +Build 1.7.10-10.13.2.1291: + lukastenbrink: + Fix fluid tanks incorrectly reading NBT when empty + + If the Empty flag was set, the tank would not correctly read the data, keeping the outdated FluidStack instead. This is especially relevant with updatable TileFluidHandler TEs. + +Build 1.8-11.14.0.1290-1.8: + LexManos: + Updated FML: + MinecraftForge/FML@a55e4124531119f1c9c023cff74cfa09b49ef0e0 Save the mod list of players in their NetworkDispatcher (Make it accessable for mods) + MinecraftForge/FML@69d479d46ae658c5a5c2c00081be3df38e38c748 Fixed getEffectiveSide() for Netty Server threads + MinecraftForge/FML@0f9a33cf14165ddd424a7d82c2178cf5854bf32f Exclude only log4j2 queue from class loader + +Build 1.8-11.14.0.1289-1.8: + Parker Young: Re-enabled Icon setting for Fluids + +Build 1.8-11.14.0.1288-1.8: + jadran.kotnik: Fixed NPE when canceling ClientChatRecievedEvent. Fixes #1644 + +Build 1.8-11.14.0.1287-1.8: + LexManos: Silently eat exceptions when getting a TE's rendering bounding box, this 'fixes' Bukkit servers screwing up world data and causing clients to crash. + +Build 1.7.10-10.13.2.1286: + asiekierka: fix shouldRefresh being too broad in tile entity update + +Build 1.8-11.14.0.1285-1.8: + Chicken-Bones: Allow blocks to render in multiple layers + +Build 1.7.10-10.13.2.1284: + lumien231: Fix versionCheck config option + +Build 1.7.10-10.13.2.1283: + CovertJaguar: Bulk Chunk Data packets should also check TileEntity.shouldRefresh(). + +Build 1.8-11.14.0.1282-1.8: + LexManos: Fix potential NPE when loading a single player world where you were saved in a unloaded dimension. Closes #1575 + LexManos: Call World.init from DimensionManager.initDimension Closes #1551 + LexManos: Made Chunk.fillBlock respect TileEntity.shouldRefresh. + LexManos: Fix value passed for Item.getModel useRemaining argument. Closes #1623 + +Build 1.8-11.14.0.1281-1.8: + lumien231: Fixes #1603: Moving the start of the update thread to the pre init of the forge mod container + +Build 1.8-11.14.0.1280-1.8: + LexManos: Fix recipies for stone variants. + +Build 1.8-11.14.0.1279-1.8: + palechip: Fix Scoreboard rendering for the sidebar. + +Build 1.8-11.14.0.1278-1.8: + LexManos: Removed exclusion of white stained glass recipes in ore dictionary. + LexManos: Added chests to the ore dictionary. + LexManos: Fix crafting of non-oak fences/gates. + LexManos: Fix MC-30864 (sending web links in chat) + +Build 1.7.10-10.13.2.1277: + mattmess1221: Fix MC-30864 (sending web links in chat) + +Build 1.7.10-10.13.2.1276: + clashsoft: Fix Stained Glass pane rendering issues + +Build 1.7.10-10.13.2.1275: + Kittychanley: Removed exclusion of white stained glass recipes in ore dictionary. Closes #1502 & #1481 + +Build 1.8-11.14.0.1274-1.8: + LexManos: Fix BlockSnapshots not firing correctly due to patch mixup in 1.8 update. Closes #1591 + +Build 1.8-11.14.0.1273-1.8: + jadran.kotnik: Fixed messages not being added to the chat history and ClientCommandHandler not being called when sleeping. + +Build 1.7.10-10.13.2.1272: + jadran.kotnik: Fixed messages not being added to the chat history and ClientCommandHandler not being called when sleeping. + +Build 1.8-11.14.0.1271-1.8: + LexManos: Fix incorrect logic in world change clumping. + LexManos: MinecraftForge/FML@5a4d362293fe70e1421d1f22c4a195944731d6ba Finish removing marker in mapping entry names. FMLMissingMappingsEvent/FMLModIdMappingEvent should fire with correct names now. + +Build 1.7.10-10.13.2.1270: + LexManos: Remove our changes to Stitcher slot allocation. + LexManos: Fix incorrect logic in world change clumping. + +Build 1.8-11.14.0.1269-1.8: + LexManos: MinecraftForge/FML@e3785c28930a218cf9374458c67c34e7fba17922 Ensure that EntitySpawn and OpenGUI packets are handled in the world thread. Also log all errors that are thrown in FutureTasks. + LexManos: Remove our changes to Stitcher slot allocation. + +Build 1.8-11.14.0.1268-1.8: + LexManos: Fix userdev for new BlockState change. + +Build 1.8-11.14.0.1267-1.8: + fry: Added model bake event (allows mods to insert custom baked models, much like TextureStitchEvent allows to load custom textures), ISmartBlock/ItemModel (ability form models to react to block/item states), Block.getExtendedState, support for unlisted properties in block states. Includes example implementation of http://imgur.com/a/FyyJX + +Build 1.8-11.14.0.1266-1.8: + LexManos: Untie ItemModelMesher from using Item Ids internally by implementing our own simple mechanics using Trove. + +Build 1.8-11.14.0.1265-1.8: + LexManos: Fix texture stitcher not using all avalible spaces. + LexManos: Fix EnumHelper for new ArmorTexture argument. + +Build 1.7.10-10.13.2.1264: + LexManos: Fix texture stitcher not using all avalible spaces. Closes #1557 + +Build 1.7.10-10.13.2.1263: + LexManos: Fixed NPE thrown when brewing event is fired and not all slots are filled. + +Build 1.8-11.14.0.1262-1.8: + LexManos: Fixed NPE thrown when brewing event is fired and not all slots are filled. Closes #1564 + LexManos: Make daylight sensor recipe use ore dictionary wooden slabs Closes #1565 + LexManos: Fix missed patch causing Dispensed Armor to go into the wrong slot. Closes #1560 + LexManos: Fixed log spam when breaking DoublePlants. Closes #1555 + +Build 1.8-11.14.0.1261-1.8: + LexManos: Unbind Shaped/Shapeless Ore Recipies from Array list to normal List. + +Build 1.8-11.14.0.1259-1.8: + LexManos: Add quartz_ore tool init, more mojang special casing -.- + LexManos: Changed ToolMaterial's repair material to ItemStack version to allow metadata sensitive versions. + LexManos: Improved Control of Enchantment.canApplyTogether() in Mod Enchantments, allowing both enchantments to determine if they can apply together. + LexManos: Expand PlayerWakupEvent to expose the three parameters passed into EntityPlayer.wakeUp. + +Build 1.7.10-10.13.2.1258: + LexManos: Add quartz_ore tool init, more mojang special casing -.- Closes #1333 and Closes #1335 + LexManos: Fix invalid argument being passed to Block.isNormalCube from World.updateNeighbors. Closes #1339 and Closes #1346 + LexManos: Changed ToolMaterial's repair material to ItemStack version to allow metadata sensitive versions. Closes #1355 + LexManos: Improved Control of Enchantment.canApplyTogether() in Mod Enchantments, allowing both enchantments to determine if they can apply together. Closes #1434 + LexManos: Expand PlayerWakupEvent to expose the three parameters passed into EntityPlayer.wakeUp. Closes #1486 + +Build 1.8-11.14.0.1257-1.8: + LexManos: Fixed bug in ServerConfigurationManager.transferPlayerToDimension where it would send the old dimension's information. + LexManos: Add "sand" to the OreDictionary + LexManos: Cleanup deprecated code, and TODOs in OreDictionary. Down-typed things from ArrayList to List. Asking for the ores with a null stack will now throw an Exception. + LexManos: Added CreateSpawnPosition event. + LexManos: Add LivingHealEvent called from EntityLivingBase.heal() + LexManos: Added PotionBrewEvent.Pre/Post. To allow for modification and cancelation of Brewing. + +Build 1.7.10-10.13.2.1256: + LexManos: Fixed bug in ServerConfigurationManager.transferPlayerToDimension where it would send the old dimension's information. Closes #1548 + LexManos: Add "sand" to the OreDictionary Closes #1487 + LexManos: Added CreateSpawnPosition event Closes #1053 + LexManos: Add LivingHealEvent called from EntityLivingBase.heal() Closes #1282 + LexManos: Added PotionBrewEvent.Pre/Post. To allow for modification and cancelation of Brewing. Closes #1248 + +Build 1.8-11.14.0.1255-1.8: + LexManos: Added Explosion Start and Detonate events to control explosion. + +Build 1.7.10-10.13.2.1254: + LexManos: Added Explosion Start and Detonate events to control explosion. Closes #1469 + +Build 1.7.10-10.13.2.1253: + draco18s: + Update RenderBlockFluid.java + + Added tessellator calls to render the block's back faces, so that the liquid properly renders when the player is immersed in the fluid. + +Build 1.8-11.14.0.1252-1.8: + LexManos: Fix finding of spawn location for mobs. Closes #1546 + LexManos: Only call blockBreak when block itself changes, not just meta. Fixes bottles poping out of brewing stands. + +Build 1.8-11.14.0.1251-1.8: + LexManos: + Updated FML: + MinecraftForge/FML@9c8ca4a4e3c4acc4980535e5c60da169b75a7810 Unlink banner block and item id. Mojang should of matched these up but they didn't -.- + MinecraftForge/FML@84a101f344b8fc21de1201fde717fbcbcba2aa79 Update Dev mcp mappings to 11-30 snapshot. + LexManos: Remove debug patch I left in. + +Build 1.8-11.14.0.1249-1.8: + LexManos: Fix stickey pistons not retracting properly. + +Build 1.8-11.14.0.1248-1.8: + LexManos: Fixed vanilla bug where top part of double plants would flicker a tifferent texture before dissapearing. + LexManos: Fix NPE with BlockSnapshots that caused items with TileEntities to be used up in creative mode. + LexManos: Fixed vanilla issue where exceptions in World tasks would not be logged. + LexManos: Fixed blocks not breaking properly when instantly destroied. + +Build 1.8-11.14.0.1247-1.8: + LexManos: Fix BlockPane's connection detection. + +Build 1.8-11.14.0.1246-1.8: + LexManos: Fixed Entity extended properties init order. Closes #1532 + LexManos: Fixed Entities not being able to climb ladders, Closes #1535 + LexManos: Fixed snow layers not being able to stack more then twice. Closes #1534 + +Build 1.8-11.14.0.1245-1.8: + LexManos: Fix interacting with entities. + +Build 1.8-11.14.0.1244-1.8: + LexManos: Fix debug screen not showing grey background. Closes #1529 + LexManos: Fix not being able to place blocks in liquids, and related issues. + LexManos: + Updated FML: + MinecraftForge/FML@36644e97714b46dbbb24416febdde1332a3e753c Finalize modded handshakes in the World tick thread. Prevents potential CMEs when login event takes to long to fire. + +Build 1.8-11.14.0.1243-1.8: + LexManos: Fix potential NPE in Block.isToolEffective + LexManos: Update RecipeSorter for new 1.8 recipies. + LexManos: Fix destroy particles not being added. Closes #1528 + +Build 1.8-11.14.0.1242-1.8: + LexManos: Fixed crash with caomparators due to wrong position. Closes #1512 + LexManos: Fix inverted logic preventing blocks from breaking. + LexManos: Fix Entityies not taking damage correctly. Closes #1511 + LexManos: Fix wrong state being passed to Block.getDrops + +Build 1.8-11.14.0.1241-1.8: + LexManos: Fix fog colors, Closes #1524 + LexManos: Fix acedential inversion causing some tress to not have leaves. Closes #1522 + LexManos: Fix creative picking a CommandBlock minecart returning wrong item. Closes #1523 + +Build 1.7.10-10.13.2.1240: + LexManos: Fix creative picking a CommandBlock minecart returning wrong item. Closes #1523 + +Build 1.8-11.14.0.1239-1.8: + LexManos: Fix local variable conflict in Forge patch and latest MCP mappings. + +Build 1.7.10-10.13.2.1236: + xcompwiz: + Fixes client-side fake rain + + Changes the updateWeather function in WorldServer to only send the + weather info to players in the correct dimension, rather than all + players on the server. This is what causes the client-side rain, as the + client believes that it has started raining locally, rather than in + another dimension. + +Build 1.7.10-10.13.2.1235: + oliver.kahrmann: + Modify WavefrontObject to allow '.' in group object names + + Blender names objects with .001 ir .002 when separating vertices or duplicating objects and the importer would crash on them. This fixes the regex to allow dots in the name. + +Build 1.7.10-10.13.2.1234: + LexManos: Player sensitive version of Block.getPickBlock Closes #1348 + +Build 1.7.10-10.13.2.1233: + LexManos: Fix slots being black due to vanilla blending leakage Forge fixes. Closes #1325 & #1242 + +Build 1.7.10-10.13.2.1232: + luacs1998: + Create CONTRIBUTING.md + + Simple file (which github will show for those making PRs) containing guidelines for making PRs. + Feel free to comment if you want/need anything added. I can pull the same thing to FML too if you'd like, Lex. + luacs1998: + Update CONTRIBUTING.md + + Add link to wiki page on contributing + +Build 1.7.10-10.13.2.1231: + Adubbz: Fixed desert list initialization. Fixes #1447 + +Build 1.7.10-10.13.2.1230: + LexManos: Fix logic error in Blodd's Snapshot capture that caused blocks to not be updated to the client. Closes #1451 + LexManos: Bump version for new RB. + +Build 1.7.10-10.13.1.1229: + Abrar Syed: implemented crowdin support + matthewprenger: Add username cache for determining a player's last known username + azanor1: + Fix for biome weights under 10 + + This solves the problem where custom mod biomes with weights under 10 + not being generated in the world. + + Cleaned up the code and made the patch smaller + +Build 1.7.10-10.13.1.1226: + LexManos: Compleetly disable stencil bits unless told not to by using the -Dforge.forceDisplayStencil=true flag. Should solve the 'menu in bottom corner' issue with Intel Integrated graphics cards. + +Build 1.7.10-10.13.1.1225: + Parker Young: Added PlayerWakeUpEvent + +Build 1.7.10-10.13.1.1224: + Adubbz: Fixed biome weights not working with non multiples of 10 + +Build 1.7.10-10.13.1.1223: + LexManos: Fix vanilla lighting issue and blending issues in achievements gui. Closes #1445 + +Build 1.7.10-10.13.1.1222: + LexManos: MinecraftForge/FML@d00feb58c762b0bbc506d79faf1ce40bc96732e9 Remove debug code that was causing console spam in Forge. + +Build 1.7.10-10.13.1.1221: + LexManos: Disable by default the Display Stencil bits. Keep FBO bits. Acording to Mumfery and ChickenBones, it should not be nessasary and should solve the 1/4 main menu issue. Use -Dforge.forceDisplayStencil=true to enable old behavior. + +Build 1.7.10-10.13.1.1220: + jadran.kotnik: Prevent client only commands from bleeding through to the server. + +Build 1.7.10-10.13.1.1219: + bloodshot: + Added PlaceEvent and MultiPlaceEvent which fires before placing a block. + + Before calling "ItemStack.tryPlaceItemInWorld", a recording flag is turned on for + setBlock to capture a blocksnapshot for each block that attempts to be placed. + + If 1 block is captured, a "BlockEvent.PlaceEvent" is fired to notify mods. + If 2 or more blocks are captured, a "BlockEvent.PlaceEvent" is fired first with the first block + captured followed by a "BlockEvent.MultiPlaceEvent" with all captured blocks. This extra event + is required for items that have the ability to place 2 or more blocks such as a BlockBed. + + If either event is cancelled, the recorded block snapshot(s), item stacksize, and item meta will + revert back to the captured snapshot(s). + If the events are not cancelled, a notification will be sent to clients and block physics will be updated. + + What this means for mods is Forge will be able to capture all player block placement automatically and fire + a PlaceEvent and/or MultiPlaceEvent. + If for whatever reason your mod does not use the standard placement methods then you will need to fire the + appropriate placement events in order to notify mods/servers. + + This commit also includes a new utility class called BlockSnapshot which is serializable. This new class is used in conjunction with + both PlaceEvent and MultiPlaceEvent in order to record a snapshot of block space before it is altered. This + allows us to restore the block(s) if an event is cancelled. The class also provides the ability to restore a snapshot + to any location using the restoreToLocation method. This should be helpful to many mods that are looking to be able + to capture block data then restore it to back to any location required. + +Build 1.7.10-10.13.1.1217: + cpw: + GIANT FML UPDATE! Bump forge revision number, and fix patches for ItemStack changes. More to come on this branch I expect. + + MinecraftForge/FML@7c5cf219042581545b6073de4e947448ffa10879 Implement STDOUT/STDERR redirection. + MinecraftForge/FML@bc78e31cb7ad4eda6e5faa173cd6b21e70a2c444 added support for \n in tooltip strings added \n test tooltip localization fixed int/double conversion error in slider entry added test slider scenario that highlighted conversion error + MinecraftForge/FML@a2908e5c596bb5502bf455d468d2b1ead0520f55 Clean up a bunch of compiler warnings. + MinecraftForge/FML@7f67523d870ae150071c67b002597542eb206725 Update realms to 1.3.2 + MinecraftForge/FML@73f23c24b85240458f352f248e885684aaff4743 Merge branch 'std-redir' of github.com:Emberwalker/FML + MinecraftForge/FML@1c6b25df740a64c94d9ba05dd7e4412515abf5bb If an IO exception comes from the datawatcher, spew it all over the console don't discard it silently. Should stop pixelmon blaming forge for their mistakes. + MinecraftForge/FML@e77da9eb2f5c58a494ed100dd4c1dd1a0c341dbf And fix the read side too, incase someone is trying to bitbang and failing. + MinecraftForge/FML@305d8950c9332c7a7f290db05e6f18ef328016e2 Make LogContext optional. This can be useful for debugging mod issues, but Apache's implementation in log4j2 is responsible for a very significant % of the overall runtime. Quite frankly this is shockingly bad performance from what is supposed to be a high performance logging framework. Anyway, until we can figure out if we can fix it, we're turning it off by default. + MinecraftForge/FML@bdfca1c8ed463a6053526c7a46a990007711e3d0 Make more noise when people screw up mod downloading and put .jar.zip in their mod folders. + MinecraftForge/FML@21084941127fc882d9968316a8f0669531e484df Add a custom version range factory method. Should hush skyboy's complaints. Closes #486 + MinecraftForge/FML@7c1e6aaa40704001231e602ceaedfa21a5df1edf Add a delegate to every item and block. this should help with renaming fun stuffs. Also, fix all the tabs from my previous commits. *sigh* + MinecraftForge/FML@61fcb4df06dc968fcc31d3e4e524e574acfdbb3b Tweak Itemstack patch to always delegate to the method call. Set the field, so it's in sync. + MinecraftForge/FML@eb8c5ab146f2eb3ad3833d40607da97831278ffb Fix nested directory for language resources. Closes MinecraftForge#1264 + MinecraftForge/FML@7c05e5f70d5387512d0bee33ef99510ee5aac739 Default collections, so that we don't crash if useDependencyInfo is true. Closes #485 + MinecraftForge/FML@9729fe23326a3d4f6b03e60b5cdaf78a484b3657 Kill net.minecraft.src warning. It hasn't served a purpose in a long time now. Closes #313 + MinecraftForge/FML@21e875ef22eef6068ccd6df1bd71cf58cba48eed AllowPlayerLogins only after the server has completed the core init tasks. Closes #372 + MinecraftForge/FML@46cfeade80ae60ad2d8cdb40c5fdfdaeeaf16d00 Add a constructor to CustomModLoadingDisplayException. Closes #387 + MinecraftForge/FML@a6eab2683a15a0cceca7a0ded6095b746cdd017b Update README.txt + MinecraftForge/FML@f75838461cf6d9c5010cbfd2d9ef5ceec03268d7 Last part is the itemstack transformer. Itemstacks should now only be loosely coupled with the items within. + MinecraftForge/FML@51f24e9e6d1bee371cf23cdfd0071de7c5175417 First draft of add alias. It is probably not properly persistent atm. + MinecraftForge/FML@2a4c6424709b20ce1e9bda0d85ce7fac47d157c2 Finally fix stupid NPE error caused by FML trying to parse the super of Object in IDEA envs. + MinecraftForge/FML@c1b1417ee168523154a0edae68c3180814eab1c7 FML now supports passing a json formatted modlist as an argument, as well as a comma separated argument list. These facilitate modpacks mostly, by meaning you don't need to duplicate mods. The modlist is arranged in the maven style, with mods referenced maven-like. + MinecraftForge/FML@3d42cda2a2cf5b24e7a25537d883260857b2107a Build.Gradle Patch + MinecraftForge/FML@20c7add8455cd16a4551ed13336a9ad4f9770cd1 Merge pull request #484 from bspkrs/master + MinecraftForge/FML@26ed4b992eb6341d52d12fb6735415ab8e3c501d Clear button list on FML fatal error screens. The hidden cancel button should not be there. Closes #497 + MinecraftForge/FML@ebe4f5c5e297d5d59ce57138810627a9c7a1b412 Merge pull request #494 from AntonBoch1244/patch-1 + MinecraftForge/FML@ad0da05f5c78d7f3c35a331e993dd6e679fc7ac9 Fix the ItemStack transformer to find the method and field so it works with srg and mcp naming. + MinecraftForge/FML@65d380181a84d35a78791e1bc3c7712cd90506f6 Extend timeout for client to 5 seconds. Should fix Forge #1322 + MinecraftForge/FML@45486a0b6dfca65c4d1dd23176d4c9d13d46b6f5 Fix almost invisible NPE in TerminalTransformer when loading a non-existant class + MinecraftForge/FML@13da3efce07653732971837709ccf4de7e4c5c8e Allow a clean way to exit the game without big ugly warnings, but with logging information available if needed. Closes #496 + MinecraftForge/FML@fda305edfea15ba2015cede72327703f273f74e3 Some more tidying up of the exit handling + MinecraftForge/FML@b087f60c3379d0767247e51cbc3f7c631fe97a08 More cleanup of exit handling, also add a couple more noise classes to the list of things ignored. + MinecraftForge/FML@d6358a466b4614cfc35b403d756fe3ef550ebf50 Cleanup override warnings + MinecraftForge/FML@af7a58b9e50dbacf63cf4b5009abc52301609e1f Update to legacylauncher 1.10 and asm 5.0.3 + MinecraftForge/FML@e6d00440a612c235013f3f92f1756811139a6de0 ItemStack swapping + MinecraftForge/FML@8597e45a0e417948db483006aa54e899f28b05ac Fix NPE from a boolean + MinecraftForge/FML@b9b9daa8a9d1cac8550561f31f118589abc0c30a Fix ups from feedback. + MinecraftForge/FML@d89165021f33fbffb4563d86b30bd261506c6ea6 Mark the promise a success in the outbound handler. + MinecraftForge/FML@2e5ccf7988385d38b964c615776f23a1718f5c27 Update for launchwrapper 1.11. Fixes java 6 compatibility. + MinecraftForge/FML@641250d8536bad3af5a036b70dae94097176b420 Fix java 8u20. Closes #501 and a bunch of other bugs too. + MinecraftForge/FML@292be72639feded03ced26d9a06a98159f7a95b7 Allow client handshake to be reset by server to support BungeeCord. + MinecraftForge/FML@092873fbe5baaee53bee67d26d2fc6d3d003f095 Merge branch 'bungeecord' of github.com:bloodmc/FML + MinecraftForge/FML@134f2f8e8865a91292386a3738bb45bad0477a4b Fix bug with entityspawn - if the entity doesn't extend livingbase, it fails to write a headyaw byte, and everything will be derped for that packet. + MinecraftForge/FML@4852de81e02e2b6c6d006abe20d8497499fdf51f Wrap the server description box a little bit shorter. Stops the overlapping. Closes #489 + cpw: + MinecraftForge/FML@4ce3e73bfe36c02b10f504f93eff1bc94d640e32 Add overloaded version of SimpleNetworkWrapper#registerMessage that takes the MessageHandler instance directly, allowing to specify the same handler for multiple messages. + MinecraftForge/FML@cbe2ccbda461ec0ecf4d776fcd19ab31930cc3f1 Add in ModType to the jar manifest. If it's present, and doesn't have value "FML" it will be skipped from the modloading cycle. This should let liteloader mods have a .jar extension. + MinecraftForge/FML@37cf0174fc62a842d132b2c2cc31e477acfba205 OK, lets make that a csv list. It'll let you be liteloader and fml in one jar file! + MinecraftForge/FML@0475b15eb1a7c35bf4959f1af40606e6ee8a9d03 Change the mods and modListFile argument handling a bit. Other tweakers will get a chance at looking at them now - they're only removed right before launch. + MinecraftForge/FML@abeac06a2e9bf8825b058fa35291165b4d1f1fb3 Two new features. ModLists can have a "parent" mod list. Circularity will result in a crash, so be careful. Mods specified in a child will override ones from a parent (using the maven group:name:classifier triple to identify - ignoring the version component) + MinecraftForge/FML@7fcfedcfef9b5fd85cd1c17aa2013fca1bacd871 Canonicalized file paths in modListFile handling with the minecraftDirectory. + MinecraftForge/FML@633fce19d4b367aed56d79e916f17296842f675c Make Keyevent also fire for key releases + MinecraftForge/FML@57ba2339b630afa22c0fdf060bf28edbf7b34d0f Merge branch 'keyup-event' of github.com:diesieben07/FML + MinecraftForge/FML@1ff048062c7f122731619258a9e5a68a6111d5dd Merge branch 'simple-netw-improve' of github.com:diesieben07/FML + cpw: MinecraftForge/FML@dc02d56195606d3ba2f1c5036fc8c0ddb67c843f Fix derp with ModType annotation. mods should load again now.. + diesieben07: Added Item#isBeaconpayment + cpw: + MinecraftForge/FML@7ab69aff2e19b349e457c1b5fcab8b3b01d22af2 Clean up import + MinecraftForge/FML@c5a90bd456230b201522c268dd9bc5e80a0b57be Is vanilla possible with this mod load, side tests. + MinecraftForge/FML@cad11f3165505e6d725411a9fc2c2ee8362f5827 Allow injecting alternative container types. This is the core support code for allowing sponge plugins! + michafla: + check for IFluidBlock (in addition to Material.liquid) when + determining render height so that non-liquids (gases) render correctly + kat.swales: Fixed Clientside GameProfile UUID being null on offline mode + cpw: + MinecraftForge/FML@63b64482e6dd4c3e2226ec002ceee549045c35ed Add jsr305 dev-time dependancy for Nullable/Nonnull annotations. Unneeded at runtime. + MinecraftForge/FML@5365f5ea3e90ec85552bdb7f1f1237c51b4ea493 Add IEventExceptionHandler for EventBus to allow special handeling exceptions that are fired while running an event. Events now track what 'phase' they are in during the execution process. Each EventPriority is a 'phase'. An exception is thrown if the event attempts to set its phase to a previous one. + +Build 1.7.10-10.13.1.1216-new: + kat.swales: Fixed Clientside GameProfile UUID being null on offline mode + +Build 1.7.10-10.13.1.1215-new: + michafla: + check for IFluidBlock (in addition to Material.liquid) when + determining render height so that non-liquids (gases) render correctly + +Build 1.7.10-10.13.1.1214-new: + cpw: + MinecraftForge/FML@7ab69aff2e19b349e457c1b5fcab8b3b01d22af2 Clean up import + MinecraftForge/FML@c5a90bd456230b201522c268dd9bc5e80a0b57be Is vanilla possible with this mod load, side tests. + MinecraftForge/FML@cad11f3165505e6d725411a9fc2c2ee8362f5827 Allow injecting alternative container types. This is the core support code for allowing sponge plugins! + +Build 1.7.10-10.13.1.1213-new: + diesieben07: Added Item#isBeaconpayment + +Build 1.7.10-10.13.1.1212-new: + cpw: MinecraftForge/FML@dc02d56195606d3ba2f1c5036fc8c0ddb67c843f Fix derp with ModType annotation. mods should load again now.. + +Build 1.7.10-10.13.1.1211-new: + cpw: + MinecraftForge/FML@4ce3e73bfe36c02b10f504f93eff1bc94d640e32 Add overloaded version of SimpleNetworkWrapper#registerMessage that takes the MessageHandler instance directly, allowing to specify the same handler for multiple messages. + MinecraftForge/FML@cbe2ccbda461ec0ecf4d776fcd19ab31930cc3f1 Add in ModType to the jar manifest. If it's present, and doesn't have value "FML" it will be skipped from the modloading cycle. This should let liteloader mods have a .jar extension. + MinecraftForge/FML@37cf0174fc62a842d132b2c2cc31e477acfba205 OK, lets make that a csv list. It'll let you be liteloader and fml in one jar file! + MinecraftForge/FML@0475b15eb1a7c35bf4959f1af40606e6ee8a9d03 Change the mods and modListFile argument handling a bit. Other tweakers will get a chance at looking at them now - they're only removed right before launch. + MinecraftForge/FML@abeac06a2e9bf8825b058fa35291165b4d1f1fb3 Two new features. ModLists can have a "parent" mod list. Circularity will result in a crash, so be careful. Mods specified in a child will override ones from a parent (using the maven group:name:classifier triple to identify - ignoring the version component) + MinecraftForge/FML@7fcfedcfef9b5fd85cd1c17aa2013fca1bacd871 Canonicalized file paths in modListFile handling with the minecraftDirectory. + MinecraftForge/FML@633fce19d4b367aed56d79e916f17296842f675c Make Keyevent also fire for key releases + MinecraftForge/FML@57ba2339b630afa22c0fdf060bf28edbf7b34d0f Merge branch 'keyup-event' of github.com:diesieben07/FML + MinecraftForge/FML@1ff048062c7f122731619258a9e5a68a6111d5dd Merge branch 'simple-netw-improve' of github.com:diesieben07/FML + +Build 1.7.10-10.13.0.1208: + zlyfire.martin: + Update README.txt + + Add in reference to running setupDecompWorkspace to get decompiled classes + +Build 1.7.10-10.13.0.1207: + bernhard.bonigl: Write the correct default value for StringList comments in the config + +Build 1.7.10-10.13.0.1206: + LexManos: Fix AIOOB in BiomeDictionary. Closes #1326 + +Build 1.7.10-10.13.0.1205: + cpw: + Attempt to properly resolve this daft metadata and TE nonsense. This might be mod impacting, if you maintain a reference to a TE via neighbour update calls - you + might see two TEs for a single setblock where previously you saw one. This is a phantom TE being created by badly written neighbour triggers - I'm looking at you + redstone. + + Anyway, with luck, this'll close a slew of bugs across Forge, IC2, MFR, TE, RC. Yeah, fun times. Thanks to LexManos, skyboy and KingLemming for helping figure this + issue out. Quite frankly, from now on, issues with phantom TEs will be mods behaving badly. Modders will need to adapt. + +Build 1.7.10-10.13.0.1204: + LexManos: Fix invalid math in GuiContainerCreative. + +Build 1.7.10-10.13.0.1203: + porcariadagata: Add ItemStack sensitive version of getItemEnchantability + +Build 1.7.10-10.13.0.1202: + LexManos: Add ability for creative tabs that have search bars to customize the text box's width, and prevent the default set of enchanted books from being displayed in those tabs. Closes #1303 Closes #1301 + +Build 1.7.10-10.13.0.1201: + abab9579: + Skylight Hooks for Minecraft Forge + + Mainly for solar/lunar eclipse. + +Build 1.7.10-10.13.0.1200: + JeanGlassmaker: Adds getLocalizedName and getUnlocalizedName to FluidStack + +Build 1.7.10-10.13.0.1199: + vazkii: Fixed items with more than 2 render passes rendering weird in first person + LexManos: Fix patch screwup in Skyboy's Fishing PR -.- + LexManos: Fix metadata for every permutation -.- Closes #1294 + t.tomkins: + Legacy Liquid Load Fix + + nbt.getString("FluidName") no longer returns null, it returns an empty string. + + This patch allows legacy liquids to be resolved once again. + +Build 1.7.10-10.13.0.1198: + DemoXin: + * Added AT for ContainerRepair.stackSizeToBeUsedInRepair (Now public) + * Added ability to AnvilUpdateEvent to alter stackSizeToBeUsedInRepair (vanilla behavior is now reproducable) + * Added AnvilRepairEvent, fired when the player removes an ItemStack from the output slot of ContainerRepair, and allows the chance to damage the anvil to be altered. + +Build 1.7.10-10.13.0.1197: + skyboy026: Add FishingHooks + skyboy026: Update FishingHooks + skyboy026: Add EntityFishHook AT + +Build 1.7.10-10.13.0.1195: + rwtema: Fixed ItemFluidContainer always draining the maximum amount, regardless of the amount remaining. + +Build 1.7.10-10.13.0.1194: + LexManos: Closes #1280 + +Build 1.7.10-10.13.0.1191: + LexManos: Fixed bug that allowed duplication of Fluids with redstone dust. Closes #1279 + +Build 1.7.10-10.13.0.1190: + idont: - Added drainFluidContainer() and getContainerCapacity() helper methods. + +Build 1.7.10-10.13.0.1189: + Adubbz: Added an event for fog rendering + +Build 1.7.10-10.13.0.1188: + cpw: + Revert old light amortization patch. It seems to be breaking chunk sending pretty badly when more + than a couple of people are online. Tests indicate it is not useful anymore. + cpw: MinecraftForge/FML@3231db9376766d619f942b6a526718daa3c68038 Sorta revert 908491d5e7ac26becdac938f38cc90d6b9d73ce1 but merge assets into the map, rather than force overwriting. Fixes skyboy's comment. + +Build 1.7.10-10.13.0.1187: + LexManos: + Updated FML: + MinecraftForge/FML@53887ac59cec8f747e21fd251f94d5a438a69114 Hacky interm solution to #1207 to buy me time to re-write FML's network protocol. + +Build 1.7.10-10.13.0.1186: + LexManos: Fixed NPE that happens sometimes when exiting the game witout fully loading a world {main a dev-time thing} + LexManos: Made skulls respect Block.isReplaceable when placing, also prevented them from deleting blocks when placed at certain angels. Closes #1233 + +Build 1.7.10-10.13.0.1185: + LexManos: + Updated FML: + MinecraftForge/FML@9d40b761974f10ec2b5868a992260792f8a98e5d Don't scan ObjectHolders if there was an error already, derpitude will ensure. + MinecraftForge/FML@76538c1781d6d6a1e4134fb317af99e6f13b46cc Add a terminal transformer and tweaker. + MinecraftForge/FML@aec9228845e50107112bd1f8693f9b4729694c8b Add ExitVisitor to the TerminalTransformer that finds and intercepts any calls to Runtime.exit or System.exit. + MinecraftForge/FML@908491d5e7ac26becdac938f38cc90d6b9d73ce1 Move LaunguageRegistry call above normal asset loading to allow resource packs to override mod's language systems. + MinecraftForge/FML@d13295e28113a1c310d5bbb90ebfe241fefabe02 Fix FMLAT manifest AccessTransformers, class loader fun! + +Build 1.7.10-10.13.0.1184: + Tyler Hancock: + Changed exception message for duplicate enchantment ids to contain the + class path for both enchantments along with the enchantmnet id that has + been duplicated. + +Build 1.7.10-10.13.0.1183: + bspkrs: + Fixed missing assignment of constructor arg (thanks @Lunatrius) + + I must have edited this out when I was cleaning up my formatting commit spam. + +Build 1.7.10-10.13.0.1182: + vincent_a_lee: + Fix comment derp (+1 squashed commits) + + Squashed commits: + + [52c40bc] Fix experience orbs spawning at 32x coordinates clientside + + Add // FORGE comment + + Add // FORGE comment to clarify the purpose of the change + +Build 1.7.10-10.13.0.1181: + vincent_a_lee: + Fix chat opacity + + Add // FORGE comment + + As per convention, add // FORGE comment to clarify the purpose of the change + +Build 1.7.10-10.13.0.1180: + LexManos: + Updated FML: + MinecraftForge/FML@ab52901b8b47a525e2719cf280327e97bad7f91e Force preferIPv4Stack to true early in the load chain to combat netty loopback issues. + MinecraftForge/FML@11893fbbb76569417a415ae794494b8c1150a716 Add system property to skip doing world backups when game registry changes. This is SEVERLY ill-advised, if you do this DO NOT ask for any support. + MinecraftForge/FML@fdb6b34b8fc3f1e0c6beb7bfb940a01a309f1603 Update authlib and realms to latest json data. + MinecraftForge/FML@b3a74882b4d0d704d7061b9d896febb59ab0c269 added slider controls for numerics. default control is textbox, but slider can be used as a custom list entry class. fixed constructor javadocs in GuiConfig + MinecraftForge/FML@7c6d1f7568885ff677e34692ff87b1f0826dfd48 Merge pull request #468 from bspkrs/master + MinecraftForge/FML@692d955c1a5b6d0b8601ae88632ef42136d37393 Update tweaker login to use authlib. + MinecraftForge/FML@c2119eb1c1246ba37304d9e565b4430ed7056db1 Update realms library to 1.3.1, and implement network latch when connecting to Realms. Tested and working. + +Build 1.7.10-10.13.0.1179: + cpw: Add a system property for the stencil, in case config is not available.. + +Build 1.7.10-10.13.0.1178: + the.country.gamer: + RenderBlockOverlayEvent patch + + Fixes skewed XYZ parameters + +Build 1.7.10-10.13.0.1177: + LexManos: Fixed Enchantment.addToBookList Closes #1160 + +Build 1.7.10-10.13.0.1176: + LexManos: Add ability for modders to designate custom biome types. And remove note about automatically registering. Closes #1167 + +Build 1.7.10-10.13.0.1175: + diesieben07: Added hook for custom PotionEffect rendering in the inventory + +Build 1.7.10-10.13.0.1174: + the.country.gamer: + Added RenderBlockOverlayEvent.java + + Adds a Forge event which controls whether an overlay is rendered. + Overlays include: First-person fire, Block (when inside a block) + and water when a player is inside a water block. + + Patched for easier manipulation of event + + Fixed for Lex + + To be squashed + + Removed Contructor + + Added block XYZ parameters + + TODO, the second block overlay event’s XYZ might not be correct + Adubbz: Enhanced the Biome Dictionary with tags based on temperature, vegetation, moisture, trees and others + +Build 1.7.10-10.13.0.1172: + LexManos: Made EmeraldOre respect isReplaceableOreGen. Closes #1157 + LexManos: Added World to ChunkProviderEvent.ReplaceBiomeBlocks, and exposed metadata to End and Nether generation events. Close #1201 + +Build 1.7.10-10.13.0.1171: + tterrag1098: + Add AchievementEvent + + Allows modders to react to players receiving achievements, and cancel + them. + + Fix indentation + + More shortening + + Down to one line... + + Remove newline + +Build 1.7.10-10.13.0.1170: + LexManos: STENCIL buffer and DEPTH buffer, attempt a fix for GL errors. + +Build 1.7.10-10.13.0.1169: + alex grabriel: Added Javadoc comments for Forge Event documentation. + +Build 1.7.10-10.13.0.1168: + LexManos: MinecraftForge/FML@ac994e178a3533aa3c2ad8359aef9e5852c27a72 Scala people test your shit. + +Build 1.7.10-10.13.0.1167: + LexManos: Remove the BLEND enable in rendering damage bars. And exclicitly fix blend states in some GUI elements. + +Build 1.7.10-10.13.0.1166: + cpw: + MinecraftForge/FML@3ee86d0f3e47249030ba2309386f9120025e95c1 updated gradle wrapper to gradle 2.0 + MinecraftForge/FML@627ae73ea655277617912df48b03288ecc79ffea Merge pull request #464 from AbrarSyed/upgradle + MinecraftForge/FML@ee38c1b3f4642c567612f88070d7f9d651994aab Fix unneeded cast causing crash in new Config GUI system. + MinecraftForge/FML@eb92c35a2fa49a0fbe35a33e31cfb58e0674f78e updated run configs for GradleStart/Server classes + MinecraftForge/FML@70dcf80410a6d12c00300c00522582ee49ac4cc8 Merge pull request #465 from AbrarSyed/upgradle + MinecraftForge/FML@0ebdbe77a2b3503db43d36aec50c98ffb8366e20 Updated Scala to latest stable + MinecraftForge/FML@ddba18e6e33a9d9c5b113b1bfc0bfc82803b2607 Merge branch 'patch-1' of github.com:Soaryn/FML into scalaupdate + MinecraftForge/FML@220a37660b2656136c634b435afee6a915fc88fe Update realms to 1.2.9 + MinecraftForge/FML@abd7d0969bed5ce3d766f52b921c2b44e8ba87d2 Merge branch 'scalaupdate' + +Build 1.7.10-10.13.0.1162: + foka_12: + Added FluidStack sensitive version for Fluid's localised name + + Would be helpful for determining names for more complex FluidStacks (with tag compounds for example) + +Build 1.7.2-10.12.2.1161-mc172: + bspkrs: dupe quotes fix for category names + +Build 1.7.10-10.13.0.1160: + Gerard Bruwn: Add RenderItemInFrameEvent + +Build 1.7.10-10.13.0.1159: + LexManos: Fixed issue where Fire's 'fizz' would not play for the person who extinguished the fire. + +Build 1.7.10-10.13.0.1158: + ohai.iChun: Fixed inverted params in FogDensity use + +Build 1.7.10-10.13.0.1157: + LexManos: Add config option to disable attempting to creat screen with Stencil Bits. + LexManos: Add config option to specify the default spawn fuzz factor for the overworld. Closes #1190 + +Build 1.7.10-10.13.0.1156: + LexManos: Fix creative inventory tabs not blending base don items rendered. Closes #1179 + +Build 1.7.10-10.13.0.1153: + bloodshot: + Fixed wrong method call in ChunkIOProvider. + + When a chunk fails to load async, we fallback to the original sync method. + In this case, it was calling the async method twice which ended up causing + a stackoverflow. + +Build 1.7.10-10.13.0.1152: + cpw: MinecraftForge/FML@db219fb287b14fea5148ecdbf07d8ff08704c66a API is now able to "provide" and "own" itself. Useful for libraries without a Mod in them. To go along with this, you can now require an API, with a version, in your mod dependency string + +Build 1.7.10-10.13.0.1151: + bspkrs: fixed compounding quotes issue with category names that require quotes when save is called more than once + +Build 1.7.10-10.13.0.1150: + LexManos: Update to 1.7.10-pre4. + LexManos: Fix flower pots not droping the items that are inside them. + cpw: MinecraftForge/FML@06ab104c9ab798af6d2726e02a238211ff8124e1 Force the descriptors to the right type for the field they're referencing. Fixes the sand issue + cpw: MinecraftForge/FML@e1529845384f4935b7c11d4d36d25db51c0b9a31 Add support for mod access transformers without a coremod requirement. Use the "FMLAT" manifest attribute, with a space separate list of files that live in the 'META-INF' directory. They should conform to standard AT formatting. + cpw: + MinecraftForge/FML@5d6dc5dce37e488188d6fc468c16e8a6183a3610 Fix up other exit points. Should stop process hangs for clean exits. + MinecraftForge/FML@8a240ec3c7e4cf4c57beabdfe9bd408e57de1bdc Merge branch 'master' into mc179 + MinecraftForge/FML@0cd5ef6bb71cda1ef6add892d1247148bf1ecc15 Fix NPE when no FMLAT is defined + cpw: + MinecraftForge/FML@701d98eafa4d55144b166d26030818baa9b2f680 Added config GUI system + MinecraftForge/FML@50164db5c13c85636c7fda0e13bae1fc0aedc745 Merge branch 'master' of github.com:bspkrs/FML into bspkrsgui + MinecraftForge/FML@7eb36a1481aea9f68fa46bc199195769b27d904b Merge branch 'bspkrsgui' + MinecraftForge/FML@96a7e14a45404449fb72af6d2d5e1efd30003318 Merge branch 'master' into mc179 + MinecraftForge/FML@f45f18b1d71e1c1d12582faa337a19e73ed5fb18 Fix accessing guava from transformers + MinecraftForge/FML@8f7adced471951c798cfa6844b0abc176c93d19b Fix library issue. mojang auth requests newer libs, so we get them at dev time. But the mojang json doesn't refer them at runtime. So there's a lib mismatch. Fortunately all are available at mojang, so we can update the json. + cpw: Fix new method with Side.CLIENT when it shouldn't have it. Closes a bunch of reports of problems. + cpw: Clean patch cruft. + cpw: Fix mcp release number + luacs1998: + Update mc version string + + Or was I not supposed to? + cpw: Fix API incompatibilities. Should mean mods will work with new config changes. + cpw: + Update to MC 1.7.10, bump to 10.13.0 + + MinecraftForge/FML@bc420dcb0b086899e2aaa218a6f5bd7e91091a90 Fix Eclipse launching attribute + MinecraftForge/FML@1e0134a1ca97a1107ebbe604e5318e6f350fe9c4 Merge pull request #455 from rumickon/feature + MinecraftForge/FML@1c5db211afc9962fedb7fd8450abc45d07807634 Update for release 1.7.10 + MinecraftForge/FML@79a0c5e55905f0f08471d026b744a563ea421236 Merge branch 'mc179' + +Build 1.7.10_pre4-10.12.2.1149-prerelease: + cpw: Fix API incompatibilities. Should mean mods will work with new config changes. + +Build 1.7.10_pre4-10.12.2.1148-prerelease: + luacs1998: + Update mc version string + + Or was I not supposed to? + +Build 1.7.2-10.12.2.1147: + cpw: Fix API incompatibilities. Should mean mods will work with new config changes. + +Build 1.7.10_pre4-10.12.2.1146-prerelease: + bspkrs: + Added support for new FML config GUI classes + refactored lots of stuff based on comments + + added Configuration.load() exception handling and logging + cpw: MinecraftForge/FML@5d6dc5dce37e488188d6fc468c16e8a6183a3610 Fix up other exit points. Should stop process hangs for clean exits. + cpw: + MinecraftForge/FML@701d98eafa4d55144b166d26030818baa9b2f680 Added config GUI system + MinecraftForge/FML@50164db5c13c85636c7fda0e13bae1fc0aedc745 Merge branch 'master' of github.com:bspkrs/FML into bspkrsgui + MinecraftForge/FML@7eb36a1481aea9f68fa46bc199195769b27d904b Merge branch 'bspkrsgui' + cpw: Fix mcp release number + +Build 1.7.2-10.12.2.1145: + bspkrs: + Added support for new FML config GUI classes + refactored lots of stuff based on comments + + added Configuration.load() exception handling and logging + cpw: + MinecraftForge/FML@701d98eafa4d55144b166d26030818baa9b2f680 Added config GUI system + MinecraftForge/FML@50164db5c13c85636c7fda0e13bae1fc0aedc745 Merge branch 'master' of github.com:bspkrs/FML into bspkrsgui + MinecraftForge/FML@7eb36a1481aea9f68fa46bc199195769b27d904b Merge branch 'bspkrsgui' + +Build 1.7.10_pre4-10.12.2.1144-prerelease: + cpw: Clean patch cruft. + +Build 1.7.10_pre4-10.12.2.1143-prerelease: + cpw: Fix new method with Side.CLIENT when it shouldn't have it. Closes a bunch of reports of problems. + +Build 1.7.10_pre4-10.12.2.1142-prerelease: + cpw: + MinecraftForge/FML@701d98eafa4d55144b166d26030818baa9b2f680 Added config GUI system + MinecraftForge/FML@50164db5c13c85636c7fda0e13bae1fc0aedc745 Merge branch 'master' of github.com:bspkrs/FML into bspkrsgui + MinecraftForge/FML@7eb36a1481aea9f68fa46bc199195769b27d904b Merge branch 'bspkrsgui' + MinecraftForge/FML@96a7e14a45404449fb72af6d2d5e1efd30003318 Merge branch 'master' into mc179 + MinecraftForge/FML@f45f18b1d71e1c1d12582faa337a19e73ed5fb18 Fix accessing guava from transformers + MinecraftForge/FML@8f7adced471951c798cfa6844b0abc176c93d19b Fix library issue. mojang auth requests newer libs, so we get them at dev time. But the mojang json doesn't refer them at runtime. So there's a lib mismatch. Fortunately all are available at mojang, so we can update the json. + +Build 1.7.10_pre4-10.12.2.1141-prerelease: + cpw: + MinecraftForge/FML@5d6dc5dce37e488188d6fc468c16e8a6183a3610 Fix up other exit points. Should stop process hangs for clean exits. + MinecraftForge/FML@8a240ec3c7e4cf4c57beabdfe9bd408e57de1bdc Merge branch 'master' into mc179 + MinecraftForge/FML@0cd5ef6bb71cda1ef6add892d1247148bf1ecc15 Fix NPE when no FMLAT is defined + +Build 1.7.10_pre4-10.12.2.1139-prerelease: + cpw: MinecraftForge/FML@e1529845384f4935b7c11d4d36d25db51c0b9a31 Add support for mod access transformers without a coremod requirement. Use the "FMLAT" manifest attribute, with a space separate list of files that live in the 'META-INF' directory. They should conform to standard AT formatting. + +Build 1.7.10_pre4-10.12.2.1138-prerelease: + cpw: MinecraftForge/FML@06ab104c9ab798af6d2726e02a238211ff8124e1 Force the descriptors to the right type for the field they're referencing. Fixes the sand issue + +Build 1.7.10-pre4-10.12.2.1135-prerelease: + LexManos: Fix flower pots not droping the items that are inside them. + +Build 1.7.2-10.12.2.1133: + Christian: Null check the Item in the supplied stack as well as the stack itself. + +Build 1.7.2-10.12.2.1132: + Christian: And fix firing for single player loading. + +Build 1.7.2-10.12.2.1131: + Christian: Fix derpity derp. + +Build 1.7.2-10.12.2.1130: + Christian: + Fire an event when a player loads or saves from disk. Mods that want to load an additional + player related file from the players dir can now do so in that event. + +Build 1.7.2-10.12.2.1129: + Christian: + Some patch offsets + MinecraftForge/FML@7219061b05db73d245405ef777b412d0787398b6 Also patch in warnings for Vec3Pool - similarly removed. + MinecraftForge/FML@dff22045587b37282adeb2167486a572f51f1f16 FML now sets a security manager (FINALLY!). It's primary purpose at this point is to catch rogue calls to System.exit so that they can cause a proper crash report, rather than silently abandoning the game. + +Build 1.7.2-10.12.2.1128: + lumien231: Fixes a server crash caused by a player joining that is in a non existent dimension + +Build 1.7.2-10.12.2.1127: + thog92: Add missing 1.7 biomes to BiomeDictionary + +Build 1.7.2-10.12.2.1126: + nemesis: + Fixed ArrayIndexOutOfBoundsException in getOreName + + Added sanity check to prevent ArrayIndexOutOfBoundsException in getOreName for negative ids. + +Build 1.7.2-10.12.2.1125: + LexManos: Fixed vines generation for hanging off of trees. + +Build 1.7.2-10.12.2.1124: + bloodshot: + Don't call ChunkDataEvent.Load async + + ChunkDataEvent.Load must be called after TE's are loaded since this is + what mods expect. The event is handled by ChunkIOProvider during + callStage2. + +Build 1.7.2-10.12.2.1123: + bloodshot: + Load chunks asynchronously for players. + + When a player triggers a chunk load via walking around or teleporting + there is no need to stop everything and get this chunk on the main thread. + The client is used to having to wait some time for this chunk and the + server doesn't immediately do anything with it except send it to the + player. At the same time chunk loading is the last major source of file IO + that still runs on the main thread. + + These two facts make it possible to offload chunks loaded for this reason + to another thread. However, not all parts of chunk loading can happen off + the main thread. For this we use the new AsynchronousExecutor system to + split chunk loading in to three pieces. The first is loading data from + disk, decompressing it, and parsing it in to an NBT structure. The second + piece is creating entities and tile entities in the chunk and adding them + to the world, this is still done on the main thread. The third piece is + informing everyone who requested a chunk load that the load is finished. + For this we register callbacks and then run them on the main thread once + the previous two stages are finished. + + There are still cases where a chunk is needed immediately and these will + still trigger chunk loading entirely on the main thread. The most obvious + case is plugins using the API to request a chunk load. We also must load + the chunk immediately when something in the world tries to access it. In + these cases we ignore any possibly pending or in progress chunk loading + that is happening asynchronously as we will have the chunk loaded by the + time they are finished. + + The hope is that overall this system will result in less CPU time and + pauses due to blocking file IO on the main thread thus giving more + consistent performance. Testing so far has shown that this also speeds up + chunk loading client side although some of this is likely to be because + we are sending less chunks at once for the client to process. + + Thanks for ammaraskar for help with the implementation of this feature. + + This commit is based off the following : + + Bukkit/CraftBukkit@b8fc6ab2c12e9b4c8d7b5370e44f23cc838014b2 + Bukkit/CraftBukkit@85f5776df2a9c827565e799f150ae8a197086a98 + Bukkit/CraftBukkit@0714971ca2a31bc729bdd78ded8c69ffb2284813 + Bukkit/CraftBukkit@7f49722f457dcc31f8cac8e011871ff1b7fd3306 + Bukkit/CraftBukkit@53ad0cf1abe9c060ef411a86e9a16352f3e5197e + +Build 1.7.2-10.12.2.1122: + antoine.lucas.33: Add missing onLivingJump calls + +Build 1.7.2-10.12.2.1121: + LexManos: Pop version for new Recomended build. + +Build 1.7.2-10.12.1.1120: + Christian: Warn when chunks are being self-recursively loaded. This can cause serious issues. Modders should watch out. + Christian: MinecraftForge/FML@1a99ec7db612f258983c6ac685da906bf7cde0a6 Deprecate getAABBPool so people can stop using it in mods. Failure to do so will result in 1.7.10 upgrade incompatibility. Use getBoundingBox instead. + +Build 1.7.2-10.12.1.1119: + LexManos: Changed dustLapis to gemLapis to make OM SHUT THE HELL UP. + +Build 1.7.2-10.12.1.1118: + LexManos: Fixed missed metadata offset, and now cache return values of getOres for speed. + +Build 1.7.2-10.12.1.1117: + cojomax99: World fog color and density can now be controlled through an event + +Build 1.7.2-10.12.1.1116: + diesieben07: Fix not being able to change target & attackDamage for EnderTeleportEvent. + +Build 1.7.2-10.12.1.1115: + LexManos: Fixed inverted player parameters in PlayerEvent.Clone, Closes #1142 Closes #1140 + +Build 1.7.2-10.12.1.1114: + LexManos: Changed EntityWolf to EntityTameable in EntityLivingBase.attackEntityFrom, to allow for more custom pets. Closes #1141 + +Build 1.7.2-10.12.1.1113: + LexManos: Rework OreDictionary's internals to be a bit more speedy to help combat modders using it inapropriatly. Closes #1022 Closes #1131 + +Build 1.7.2-10.12.1.1112: + Christian: + MinecraftForge/FML@e3ce211cc798f4d86ca6f974d9ba8b4e389b4dc9 Nullcheck the dispatchers on players. Should stop some crashes when spamming connectivity. + MinecraftForge/FML@480bf2c1d078038bb59c4254a01a5af685c7cb22 Fix REPLY handling in SimpleNetworkWrapper. Closes #440 + +Build 1.7.2-10.12.1.1111: + LexManos: + Fix inverted parameters in OreDictionary.getOreID Closes #1123 + -.- Closes #1120 + +Build 1.7.2-10.12.1.1110: + Adubbz: Greatly simplified the addition of new biomes to the default world + +Build 1.7.2-10.12.1.1109: + LexManos: Added Noteblock change and play events. Closes #1027 #1025 + +Build 1.7.2-10.12.1.1108: + LexManos: Add target world to PlayerInteractEvent for potential 'cross dimension' interaction such as LittleBlocks. Closes #1071 + +Build 1.7.2-10.12.1.1107: + LexManos: Add a couple of localizations to Forge added strings. Closes #1068 + +Build 1.7.2-10.12.1.1106: + rwtema: Fixed setBlock not using the location-specific version of getLightOpacity() + +Build 1.7.2-10.12.1.1105: + LexManos: Filter all vanilla blocks that we missed through Forge's getDrops and BlockDrops events. As well as implemented IShearable for DoublePlants and DeadBushes. Mojang really should generic out some of this stuff instead of repeating logic all over the place! + +Build 1.7.2-10.12.1.1104: + LexManos: Fix patch fuzz + LexManos: Fixed GuiContainer calling button.mouseReleased. Closes #1116 + LexManos: Add comment to RotationHelper telling modders where to actually look, closes #1115 + +Build 1.7.2-10.12.1.1101: + diesieben07: + Add PlayerEvent.StartTracking and .StopTracking & make trackedEntityIDs visible + & Update, as discussed on IRC (squash) + +Build 1.7.2-10.12.1.1100: + vilim.lendvaj: Make finite fluid blocks drainable + +Build 1.7.2-10.12.1.1099: + LexManos: Fix enum helpers for EnumRarity {moved to Commn and changed paramter} and EnumCreatureType {new parameter} and added junit test for them. Closes #1009 + +Build 1.7.2-10.12.1.1098: + Christian: MinecraftForge/FML@1d41aa978d41267e4040ec449e10f49a20edd4fa Fix the side for the compatibility check. Should result in green ticks finally! + +Build 1.7.2-10.12.1.1097: + LexManos: Change IShearable JavaDoc by one line so Anti would shut up. Closes #1054 + +Build 1.7.2-10.12.1.1096: + Christian: MinecraftForge/FML@4512f8e5e316ddaf6a4fe35470f1f88dcdddae1a Warn when the objectholder finds nothing in the registry. Helps debug mismatched names. Also, actually make the scoping thing work with objectholder + +Build 1.7.2-10.12.1.1095: + fry: Added world display list render events + +Build 1.7.2-10.12.1.1094: + foka_12: + Fix getOreIDs not using the wildcard value correctly + + You can see it's not working by requesting the ore IDs from lapis, and you'll see the name "dye" is missing (and that's the one registered using the wildcard). + + Fixed it by inverting the order of the item stack parameters. + +Build 1.7.2-10.12.1.1093: + LexManos: Prevent duplciates in registered Ores in the OreDictionary and clean up some of the code, add new function to return all ores the specified ItemStack satisfies. Closes #1102 + +Build 1.7.2-10.12.1.1092: + vilim.lendvaj: + Fix fluid blocks + For https://github.com/BuildCraft/BuildCraft/issues/1843 . + +Build 1.7.2-10.12.1.1091: + LexManos: + Updated FML: + MinecraftForge/FML@3aba56440aa7a95f6431efcdcb5c127ebafc8891 lastIndexOf, Note Don't code while sick. + +Build 1.7.2-10.12.1.1090: + LexManos: + Updated FML: + MinecraftForge/FML@c828bb63c57cb10c23d9b1c3a6934e9f9ddba37b Make AccessTransformer change INVOKESPECIAL to INVOKEVIRTUAL when making methods visible + MinecraftForge/FML@a9aa468457a1eeed3366505b93e36da654610f05 Merge pull request #431 from diesieben07/at-invokevirtual + MinecraftForge/FML@31d726abad0dec6d1b853e9adf6a01580aee2af4 Fix the null networkHandler in the MessageContext + MinecraftForge/FML@725d988e36a7b104b9f5d8ae2daf993ac12af5bd Add in the objectholder, autopopulated by FML based on simple rules. Allows for reference driven substitution of mod blocks and items based on their server running state. + MinecraftForge/FML@f07bf5cb30a20ca9f62337512e936cfefcfbf0c4 Fixed deobfusication of nested inner classes. And removed legacy ModLoader remaps. + LexManos: + Updated FML: + MinecraftForge/FML@3a687f48b9606b4f9179d63ef0b831a25821ff8f -.- Save File First. My Bad. + +Build 1.7.2-10.12.1.1088: + LexManos: Add Glass, Colored glass, ore storage blocks, alines, and a few others to ore dicitonary. Closes #1011 + +Build 1.7.2-10.12.1.1087: + clashsoft: Update GuiScreen.java.patch + LexManos: Add new PlayerEvent.Clone called when an EntityPlayer is cloned via dimension travil or respawn. + LexManos: Made WorldGenShrub respect Block.canSustainPlant, Closes #1096 + +Build 1.7.2-10.12.1.1085: + delma: + Fluid events now know how much fluid is moved + + Added amount that is being filled/drained to the FluidEvent + + Added constructors without amount to ensure backwards compability + + Added deprecation to amountless constructors + +Build 1.7.2-10.12.1.1084: + LexManos: Fixed Stems not droping a random number of seeds based on metadata, Closes #1087 + +Build 1.7.2-10.12.1.1083: + xcompwiz: + Bug Fixes to Biome Decoration and Chunk + Fixes issue with biome decoration crashing on worlds with exposed void + Fixes same issue in JungleBiome decoration + Fixes forge bug in getting lighting from a block in chunk + +Build 1.7.2-10.12.1.1082: + LexManos: In Flower Forest biome a Poppy should spawn instead of Blue Orchid Closes #1078 + LexManos: Added Farmland to PLAINS type plants as that has changed in 1.7. Also made BlockTallGrass call it's super.canBlockStay to better support custom soils. Closes #1077 + LexManos: Added accessible instance to RenderBlocks and RenderItem for modders to use who don't wish to create there own instance. Warning: Other modders may influance the transient state of the instance, BE WEARY MODDERS. + +Build 1.7.2-10.12.1.1081: + Christian: MinecraftForge/FML@43e3ee1af1cab54db238dab4994076fdbe68bc6a Swap Listenerlist constructor around. Should fix parent resizing issue? + +Build 1.7.2-10.12.1.1080: + Christian: MinecraftForge/FML@70570a863ffa6a3ba7e2dd30b471bb47615b8bf8 Fix up possible CME + +Build 1.7.2-10.12.1.1079: + Christian: MinecraftForge/FML@503da3a2577a069b7847c158a27e8316c85ed852 Don't consider null NetworkDispatchers. This should fix a bunch of fakeplayer issues. + +Build 1.7.2-10.12.1.1078: + LexManos: Fully clear DimensionManager's DimensionID bitset when world is loaded. Closes #1074 + LexManos: Fixed No Blue Orchids spawn in swamp when using bone meal Closes #1072 + +Build 1.7.2-10.12.1.1077: + Christian: Fix forge validating strict versions on remote connections. + +Build 1.7.2-10.12.1.1076: + Christian: + MinecraftForge/FML@a8cbef2321a8e1bdfac56476bdfb5b306f71d38b Finally hopefully fully kills the race condition causing a classcast on slow machines. + MinecraftForge/FML@8dbd1ae0a177a556d03630a059242a2ee7f45e55 Fix ObjectIntIdentityMap sporadically matching non-identical objects. + MinecraftForge/FML@42713c66e565a26e963099baa838800f250089c3 Merge pull request #426 from sfPlayer1/master + +Build 1.7.2-10.12.1.1075: + Christian: + MinecraftForge/FML@d8b6adb2598ce144568a0aaf26fa8b988c028b7c Add a helper for casting some common collection types into generic form + MinecraftForge/FML@5275cea844a6afacc0deb41d153f01c1c25bb924 Try and see if there is anything to the identityHashCode collision hypothesis. If you see this in your error messages, kindly let us know! + +Build 1.7.2-10.12.1.1074: + LexManos: + Updated FML: + MinecraftForge/FML@a70308ef41f1e24074ea718f64caf75b8d6acba7 Update mcmod.info + MinecraftForge/FML@8555344eb33e4f0cc676defdb7391a24ebd5677d updated wrapper to gradle 1.12 + MinecraftForge/FML@1d5fc60f82e911c1abfbebbe781316126c02c987 Merge pull request #411 from matthewprenger/master + MinecraftForge/FML@3612ad0c25d103ba9bc81b32e8ecfef2dfc1cadc Attempt to fix another race condition related to reading NetClientHandler. + MinecraftForge/FML@c73a2076e3dd5d1f60c2fe2f589109cefa2dc6ce Fix potential rance condition in connecting to vanilla servers as well. And move latch into client side only. + MinecraftForge/FML@1436ac2f14fbdb48777c90b1b93378108c9cbf36 Fine use FMLCommonHandler. + MinecraftForge/FML@542e9acec1016c950c6f80af0c9da3190691359b documented dependencies + MinecraftForge/FML@ddc2cfbe864bd377232dbd1aa65df6e710d4639d Merge pull request #402 from AbrarSyed/patch-1 + MinecraftForge/FML@362ec8dee7ed2c291a8ed287c52eacdd80582eff Merge pull request #419 from Thog92/master + MinecraftForge/FML@b9de9ebc960bbf26e7aee570701aa4c226252fee Revert "Fix refreshResources not happening if an error occurs." + MinecraftForge/FML@738ce1d7cd5575269375066586d0a37881c536e2 Re-add removed genericiterable to clean a warning + MinecraftForge/FML@b0eb1ef7c6f4a63689898bf28f28e84d2dbae6e7 Split loadmods into loadmods and preinitmods, to allow resource loading to occur *always* between the two phases. This should fix mods not being able to access resources during preinit. + MinecraftForge/FML@de546bdf6cbeadb612cd6385bac8d54480073496 Clean up some missing generic info + MinecraftForge/FML@cd43eacbb25bc9cc0e81138844fa3aa7fd133037 Ensure that the loadcontroller is ready to preinit. + MinecraftForge/FML@f2fe80dc36972fe9db57e700380b6869abbc1832 Fixed default network mod checking to allow client side mods without the server side. Mods wishing to REQUIRE server side components must specify a custom check handler using @NetworkCheckHandler + MinecraftForge/FML@0c36868f92a3516c83ae363e13e5cb1db81236d1 Fix network disconnect with message on the client side in NetworkDispatcher. + +Build 1.7.2-10.12.1.1073: + bspkrs: Fixed unforeseen NPE + +Build 1.7.2-10.12.1.1072: + LexManos: + Revert "Implemented hashCode and equals in ItemStack, Closes #986" + + This reverts commit 0b01545a03942abca7b7ea28030be81e2ebeaa59. + +Build 1.7.2-10.12.1.1071: + LexManos: Implemented hashCode and equals in ItemStack, Closes #986 + +Build 1.7.2-10.12.1.1070: + LexManos: Fix extended entity properties being lost when leaving the end, This introduces the concept of calling IExtendedEntityProperties.init when entites/worlds change. Lets see if mods explode. + +Build 1.7.2-10.12.1.1069: + bspkrs: New GuiScreen events and a new ElementType DEBUG for RenderGameOverlayEvent + +Build 1.7.2-10.12.1.1068: + LexManos: Added new hook to WeightedRandom.getItem that allows for use of custom rnadom generators, prevents redundant code in mods. + +Build 1.7.2-10.12.1.1067: + LexManos: + Updated FML: + MinecraftForge/FML@2c56c32c5aa8842cfadaf8c237396cdb75673909 Fix saving backups with the raw name. Fix air block not being assigned as the default. + MinecraftForge/FML@d0f8073fa51db7426d5ded373f3404fa60d722f0 Merge pull request #413 from sfPlayer1/master + MinecraftForge/FML@810b1f3075e6061ab189e1f6975bd77b20040d71 Clean some generic warnings up. Make a generic list handler. Helpful for others I think too. + MinecraftForge/FML@fff86ee9d35874bdf77a1eaabe77615441644064 Fix refreshResources not happening if an error occurs. + MinecraftForge/FML@76d8d0e870a4e389167634283984dc10abb08e84 Fix mod version checking + MinecraftForge/FML@b84d0760ae47832e5b1e4d50237b582b2d50d520 Fix display of mod status at the server + MinecraftForge/FML@251af1d09dfbf636e2fb3f323a5345c81cc07aea Fixed memory leak on the client caused by Netty holding references to the World. + LexManos: Fixed hard references in WorldGenBigTree and ForgeCommand that caused worlds to leak in the client. + +Build 1.7.2-10.12.1.1066: + lhb: + Fix for Block.getExplosionResistance getting passed the wrong parameters + + It is being sent the x, x, y coordinates instead of x, y, z + +Build 1.7.2-10.12.1.1065: + CovertJaguar: + Fix issue with flexible rail return value + + Between 1.6 and 1.7 the return value to BlockRailBase.isFlexibleRail() + was inverted. While this is not a huge deal and could be worked around + by simply inverting your return value, it does mean its no longer + consistent with the function name and javadocs. + bioxx2007: Adds a new ReplaceBiomeBlocks Event constructor that supplies the metadata array if applicable and updates the ChunkProviderGenerate class to pass in the metadata array. + +Build 1.7.2-10.12.1.1061: + bspkrs: get a spelling checker :P + +Build 1.7.2-10.12.1.1060: + LexManos: Bump Forge version in prep for release. + +Build 1.7.2-10.12.0.1059: + LexManos: Try and fix invalid framebuffer depth/stencil setup, Thanks Ivoforce. Closes #1032 + +Build 1.7.2-10.12.0.1058: + apricefrench2d: + Fix infinite loop in RecipeSorter + + If recipe is multiple levels of inheritance from Object and not categorized, cls=cls.getSuperclass(); needs to be repeated more than once. It must therefore be moved to inside the while loop. + +Build 1.7.2-10.12.0.1057: + ohai.iChun: Fixes stencil buffers on platforms not supporting OpenGL 3.0 or higher. + +Build 1.7.2-10.12.0.1056: + LexManos: + Small wording change in license to allow for specifc differnet licnense contributions. + + Updated FML: + MinecraftForge/FML@e58562d3edfd1cd37fdc0a9e54181aed7433fdff Fix remaining issue with autoassigned Block and Item IDs overlapping. + MinecraftForge/FML@a82195772e539437911c25508168cb607659bc71 Registry: Block IDs after failing to find a mapping for them + MinecraftForge/FML@fd9389015fd5c6150155531bf1fffb38cfe9d551 Fix FMLMissingMappingsEvent.get + MinecraftForge/FML@5eebd4df718d65ac8426deba61e1ebb6ae2fde18 Registry: Implement support for remapping blocks/items to a new name. + MinecraftForge/FML@7325aa5033e7a5b5db79340777dd7a1c763315a0 Registry: cleanup, fix missing id error GUI formatting + MinecraftForge/FML@eb29d651ebda7086fe6d1f716295b087e2c17e6f Fix old 1.7 worlds with broken ID mappings + MinecraftForge/FML@6fd3c12a4a15a5cf38c421a94576a5cacd3fb7c1 Registry: allow handling missing blocks/items regardless of the mod id + MinecraftForge/FML@038fa17ad33aeba276db84ab170504fce884c1e7 Merge branch 'master' of https://github.com/MinecraftForge/FML + MinecraftForge/FML@49c623f59c440ba177adf2d76332ecee25e12236 initial attempt at a better way to ask the user in case of startup issues + MinecraftForge/FML@9be92dcfcb1c737025397c92b18ed027a6c7f4fa Registry: Complain about bogus registrations + MinecraftForge/FML@fd6d55afcc4f4c650c143ad43e09fbdc2cb9d850 Registry: Allow ignoring missing mods from the GUI, with confirm+backup + Registry: Add confirm+backup for automated corrupted id table fixup Require the user to confirm loading from a backup level.dat + MinecraftForge/FML@c47fc3b382434d435050b4ee02a02550b81f5717 Enable custom gui rendering only as required + MinecraftForge/FML@f77632df35dbf53fb31420fa86e6792f13257020 Remove unneeded entity spawn debug logging. + MinecraftForge/FML@c7adb42199a0684d8748451d39deb8326c0a2194 Registry: Repair mismatched ItemBlocks as well Fix a few misc issues + MinecraftForge/FML@c8a245a985779fd2545ee4b58a93270973aeb435 Registry: Fix debug info + MinecraftForge/FML@c8a0b72eba9265be608670424e1bd835a9d2f1e2 Registry: Complain about missing mods when repairing broken worlds Registry: Reduce console spam + MinecraftForge/FML@8e44006f432f1b36b826ff0469d99986a6051e4b Registry: Protect against putObject misuse, handle duplicate registrations better + MinecraftForge/FML@294c93212cd9f30c50b9d1a3b048a6141c45cdea Registry: Add support for registering ItemBlocks before their Blocks + MinecraftForge/FML@3b42b33b6ec4020b5032cae06760053ed135fae8 Merge pull request #400 from sfPlayer1/master + +Build 1.7.2-10.12.0.1055: + traincrazyb: + Small Fix: Held Items & Multiple Render Passes + + Passes beyond 1 now have the correct icon. + +Build 1.7.2-10.12.0.1054: + LexManos: Fix line offset in Minecraft patch, and mix RenderPlayer looping once to many on multi-pass items. + +Build 1.7.2-10.12.0.1053: + t.tomkins: Update PlaySoundEvent17.java + +Build 1.7.2-10.12.0.1052: + LexManos: Update access transformer, Closes #951 and #1021 + LexManos: Add NPE protection to GuiingameMenu.actionPerformed, Closes #961 + LexManos: Finally do SoundSystem workup for 1.7, Closes #982 + +Build 1.7.2-10.12.0.1051: + LexManos: Use BiomeGenBase's array size instead of hardcoding it in BiomeDictionary. Closes #871 + LexManos: Fix potential threading issue if FluidRegistry.loopupFluidForBlock is called from two threads at the same time before being setup. Closes #936 + +Build 1.7.2-10.12.0.1050: + LexManos: New hook to truely seperate the display of the 'durability' bar from the current / max durability. Allowing modders to control that display easier. + LexManos: ItemStack sensitive version of Item.getAttributeModifiers, Closes #816 + LexManos: Add ANIMALS tpe to PopulateChunkEvent.Populate Custom providers should call this function if they spawn animals curing population. Closes #790 + LexManos: Add AnvilUpdateEvent which is fired when a user places a item in both input slots of a Anvil and allows modders to control the output. Closes #838 + +Build 1.7.2-10.12.0.1049: + LexManos: Add position to BreakSpeed event. Closes #621 + LexManos: Advanced Model Loader available server-side for data driven models. To be cleanuped and re-evaluated in 1.8. Closes #773 + +Build 1.7.2-10.12.0.1048: + Abrar Syed: COnverted patches to SRG names + Abrar Syed: updated for ForgeGradle 1.2 + Abrar Syed: added .exe file.. fixed a bunch of patches + Abrar Syed: updated FML to latest master + Abrar Syed: fixed remaining noop patches and exc derp + LexManos: Update patches for new Fixed FernFlower used in FG 1.2. + +Build 1.7.2-10.12.0.1047: + reflex_ion: + This correctly uses the world height less one block for placement of a + Door. + + Required for placing doors inside a littleblocks area. + +Build 1.7.2-10.12.0.1046: + LexManos: MinecraftForge/FML@ef07de4f65ea16e1db1467845e316cb4c7d01a1f Fix hard link to DedicatedSerever when opening a LAN connection causing stalls on connecting. + +Build 1.7.2-10.12.0.1045: + LexManos: -.- Both null combinations. + +Build 1.7.2-10.12.0.1044: + LexManos: properly implement equals, sod off Player. + +Build 1.7.2-10.12.0.1043: + LexManos: Implement simple hash based equals in Fluid ContainerKey. + +Build 1.7.2-10.12.0.1042: + LexManos: Make StructureVillagePieces.Village public. + +Build 1.7.2-10.12.0.1041: + ohai.iChun: Fixes stencil bits not existing in Minecraft's framebuffer causing stencil test to not work. + +Build 1.7.2-10.12.0.1040: + LexManos: + Updated FML: + MinecraftForge/FML@e8b60441ccca8cccdc130560b4c8bf400aebc605 Reload game settings after mod loading is finished to capture mod keybindings. Closes #378 + MinecraftForge/FML@399770e572c9177babfb65a27280253023db2d9e Kill the modEventTypes list, register anything that extends FMLEvent, Fixes MissingMappingEvent handler, and any futureevents added. + MinecraftForge/FML@b7ad532ab5eb3e00d77ffde946d25675c9f69cf7 Re-enable post initalize texture pack reloading to allow Icons to be registerd through any init phase. + +Build 1.7.2-10.12.0.1039: + t.tomkins: + Small Fix: Held Items & Multiple Render Passes + + Passes beyond 1 now have the correct icon. + DemoXin: * Added Ore Dictionary entries and recipe replacements for Diamond, Emerald, Crops, Redstone, and Glowstone + DemoXin: * Fixed Items.glowstone to Items.glowstone_dust + +Build 1.7.2-10.12.0.1034: + LexManos: Fixed hashcode in FluidContainerRegistry, still needs a redesign. Closes #967 + +Build 1.7.2-10.12.0.1033: + ohai.iChun: Add cancelable RenderHandEvent. + +Build 1.7.2-10.12.0.1032: + LexManos: Fix a typo in our tile entity fix causing it to be ineffective. + +Build 1.7.2-10.12.0.1031: + LexManos: Fix items with color rendering incorrectly. + +Build 1.7.2-10.12.0.1030: + Christian: + Update Forge for patch changes + + MinecraftForge/FML@064b66af3d6c92b19821b88ec26cbb59577d68b4 Prevent players from logging in until server has finished starting. + MinecraftForge/FML@2aa73afa15908dadb0a033c49deb0ffefad2f265 Fix ExampleMod.java for build #1024+ + MinecraftForge/FML@c890206268da3c594d97198f5426b52ff6b8460c Try and handle removal of mods a bit better. Currently no way to allow a world which has missing blocks to load - but i have the code in place to allow it i think. + MinecraftForge/FML@995c204338cd601e118396d4b4ef8feb6e759037 Fix failing to load a world with missing mod blocks and items. There will be a way to force worlds to load when stuff is missing, but for right now, it will fail as this is "world safe". + MinecraftForge/FML@fa5f4c884272f415933329a9e914e0b7d052e31a Some argumentation + MinecraftForge/FML@45409bfa0c136078823a1aef1358396d92a269ee Prevent player dat files getting reset during disconnects. + MinecraftForge/FML@33100d6bab654a4bd59701b1ec2bf91caa3399da Merge pull request #371 from bl4ckscor3/patch-1 + MinecraftForge/FML@572d32358ab11e5916d91c4c7b9c04a70cfed2f6 Merge pull request #373 from bloodmc/master + MinecraftForge/FML@d0dd05a15c2eca9eabd308319c2ed85cb632922b FML expands S3F to support payloads up to 2 megs in size. Should be transparent + +Build 1.7.2-10.12.0.1029: + LexManos: Fixeed a missed -1 in SpawnerAnimals patch. Thanks Blood. + +Build 1.7.2-10.12.0.1028: + LexManos: Cull FakePlayers when worlds are unloaded. + +Build 1.7.2-10.12.0.1027: + LexManos: Use correct tag types when reading Forced Chunk data. Fixes ticket loading. Closes #964 + +Build 1.7.2-10.12.0.1026: + LexManos: Fixed FluidContainerRegisry.contansFluid closes #845 + +Build 1.7.2-10.12.0.1025: + LexManos: Fix AIOOB error with Endermen and blocks >256. Also better support for ID remapping. More to come later. + +Build 1.7.2-10.12.0.1024: + LexManos: + Updated FML: + MinecraftForge/FML@03fb1879d72fbd347badc140fed6c2c3191d2990 Fix obf error when right clicking a Empty Map. + MinecraftForge/FML@6bb9b8b9532b276450d03a3419e0da016aecead8 Clean up FMLEventChannel. Closes #367. + MinecraftForge/FML@b7b3450dcd123ab5df6b3693c9c2123bc3846b88 Update MCP mapping snapshot to latest crowdsourced names. + MinecraftForge/FML@8c9e8b52708bd0630303f8b5dc184ab60e2553a1 Fix isRemote, this is integral to so many parts of the code, everyone knows it by this name, People should not change it. + LexManos: Add the beginnings of a Constants class, to document/clean some of the magic numbers that are in the MC code base. + +Build 1.7.2-10.12.0.1023: + Christian: + MinecraftForge/FML@d87822ad8519da1c808e48bcc0a1bf8eb15c0095 Bump gradle wrapper to 1.10 + MinecraftForge/FML@359ac3ca2a941d70709168fbbbc0725c861668dd Ensure we check both item and block registries when finding valid IDs. Should fix #365 + MinecraftForge/FML@cee0f0b81179d307059843f08401f8700fb3ddb2 Tweak so that writing to the context will automatically send a message back to the originator in handshakeestablished. + Christian: Add a discriminator for fluididspacket. remove extraneous channel handler. + Christian: Fix up the event handler so it knows it's owned by forge + +Build 1.7.2-10.12.0.1022: + LexManos: Uncomment aa few FluidRegistry entries. + LexManos: Fixed issue where enchantment effects caused slight rendeirng issue. + LexManos: Fixed missed parens causing trapdoors to fall off incorrectly. + +Build 1.7.2-10.12.0.1021: + LexManos: Inital Fluid system update, untested. Still in progress. + +Build 1.7.2-10.12.0.1020: + LexManos: Fix NPE when breaking ice. + LexManos: Attempt a AIOOB error fix in tesselator when there are alot of transparent blocks in the rendering range. + +Build 1.7.2-10.12.0.1019: + Christian: + MinecraftForge/FML@544320b8d239df4a5ee2b3a7ec331ce2ec0a2c09 Beginning of a saveinspectionhandler. + MinecraftForge/FML@ab199c5811fe2d831592601d4f77691fbf82d1b8 Try harder to find a mod container. + MinecraftForge/FML@8633d780c925ebb719c37ac52e2f3db5f9957895 And make a loud message if there isn't a modcontainer found, substitute Minecraft. In general, this can only happen for coremods not properly registering their code. Closes #363 + +Build 1.7.2-10.12.0.1018: + LexManos: Fix imporerly efficient tools breaking blocks to fast. + +Build 1.7.2-10.12.0.1017: + LexManos: Fixed potential NPE in SlotCrafting, and added ItemStack sensitive version fo hasContainerItem. Closes #854 + LexManos: Adds a WeatherRender in the style of SkyRender, Closes #844 + +Build 1.7.2-10.12.0.1016: + LexManos: Move change of metadata to immediatly after change of Block, should prevent any 'invalid' tile entities from breaking created. Reference: #897 + +Build 1.7.2-10.12.0.1015: + LexManos: New PlayerUseItemEvents, Start, Stop, Tick and Finish. See PlayerUseItemEvent.java for more details. Closes #924 + +Build 1.7.2-10.12.0.1014: + LexManos: Make ItemBlock.field_150939_a public, closes #945 + LexManos: Missing EntityAITasks.tasks and MapgGenStructreIO register ATs, Closes #949 + LexManos: Implement PlayerPickupXpEvent, fired when a player aquires XP from a EntityXPOrb. Closes #942 + LexManos: Exclude cobblestone slab recipe from ore dictification, closes #940 + LexManos: Add the ability for custom records to have finer control over there sound resource location. Closes #933 + LexManos: Don't short circuit item icons for multiple render passes while being used. Closes #929 + +Build 1.7.2-10.12.0.1013: + LexManos: Fix Furnace stopping on 63rd Item, Closes #947 + +Build 1.7.2-10.12.0.1012: + Christian: + MinecraftForge/FML@b6d95d704b65dd8232ec8ddd333de378db8fe161 Name the log files properly. fml-junk is an early startup annoyance I can't kill because log4j2. + MinecraftForge/FML@8692ca17d13eda036b5ef996ec8e8706e7707d80 Log4j2 logging context for things. This should help add context when things go wrong in mods. + MinecraftForge/FML@a7ca131a337b5f0d4fc6f438626ac2d5b7771b3c And don't spam NONE everywhere + MinecraftForge/FML@741e172ffe163f0dd3018e1474af46ef0696396a Log4j2 doesn't need debug level logging for itself anymore + +Build 1.7.2-10.12.0.1011: + Christian: MinecraftForge/FML@458b0620b43116c943549a0f060c7e8830c2d77a Log the bad packet in a prettier way. Also, don't show the authlib debug data in the log file. + +Build 1.7.2-10.12.0.1010: + LexManos: Add BookCloning to the recipe sorter. + +Build 1.7.2-10.12.0.1009: + Christian: + MinecraftForge/FML@9a8d16b66e67691a4c83a9e1e236304e9f6d5139 Fix log4j2 config. Fix server gui to *show* logging. Fix log spamminess in the console. Fix bug in servergui that can cause deadlock. + MinecraftForge/FML@a355eecb2c14123964c6ae2402a0933d57ae9736 Add in error logging for outbound messages. Fix bug with indexedcodec NPE + MinecraftForge/FML@1c793abe0eef6846f681c9673019b0ebc49caaaf Fix derp with networkcheck + MinecraftForge/FML@675b5a07788ada17bc26a9c4f26598e77d2098cf And turn down some more logging, now we have useful logging back again.. + +Build 1.7.2-10.12.0.1008: + Christian: + MinecraftForge/FML@4aa2416ce5dcd8e77761703c018d1e7d08464025 Propagate Optional method removal to trait implementation classes + MinecraftForge/FML@fc025a7b73d9b3f46ecf2257227657592f5506b5 Logging Changes + MinecraftForge/FML@f0132a6f3b47e746a1a7df3ef84f4be989f140dd changed fml log level to all + MinecraftForge/FML@f23eba4352c38fd21e04e81f3db72c6cafe65a36 put max number of FML log files to 3 + MinecraftForge/FML@449ac98b77025eba38a75d0242113fffe26a8cf9 SSP Worlds updating from 1.6 will now pop a warning message before loading, and will capture a timestamped zip file in the minecraft dir before starting to load. Allows for people to test updates. + MinecraftForge/FML@3557fe31c92ea8d76c90052f9b8b6da963300c4f Throw an exception when discriminator is not found + MinecraftForge/FML@25240457283ba40c32022c97fc982c2ff4408e46 Make NetworkEventFiringHandler sharable + MinecraftForge/FML@dfc0899ec66f87502b5727939ac2f0ad0fabf89f Merge pull request #357 from jk-5/sharable + MinecraftForge/FML@79d42fca8d6b9d73204890ef0edb9d73cf075d87 Merge pull request #355 from jk-5/errorhandling + MinecraftForge/FML@7907e16e96de21e8ba536906ae71adcf02bfa535 Add a type adapter for artifact version. Should fix #354 + MinecraftForge/FML@7ac5bddbc3c227e0ed9385904a2bd9621078e2de Allow indexed messages to validate themselves. Also, catch exceptions from an embedded channel, and cause them to close the connection. It's ugly, but it means that the client doesn't crash if it connects to a screwy bungycord that's trying a 1.6 handshake for some reason. + MinecraftForge/FML@5adacc3b336bacbe30aa06175ef80c3aac08a62a Check the mod, not it's container, in the check handler. Closes #358 + MinecraftForge/FML@3d26f28bcf3e79e1f5fe20fcf056c604487dc35b Allow connection when server is apparently offline. Might allow :NOFML circumvention though. Hmmm. Closes #359 + MinecraftForge/FML@a62374d4aceac1c4ab39b3c0bae624ccbca65b6b findBlock should now return null, not the default block, if the thing being looked for is not found. Closes #352 + MinecraftForge/FML@6a695c4348d062af50b8cf5208530fc5036eba17 Try and stop the epic channel closed spam at close time. Closes #353 + MinecraftForge/FML@35a38d7840a5d0cd842005822c4ec6a9d3b65b6a Make sidedproxy support non-public fields. Closes #344 + MinecraftForge/FML@9d2e089df692655df04315a3822f43140015f3af Merge branch 'logging' of github.com:AbrarSyed/FML into abrar-borked + MinecraftForge/FML@79b04898d43d354714e09ce7e66efb5357ebcf61 And restore suppressions. ABRAR, DON'T TIDY CODE!!!! + MinecraftForge/FML@3dfb54e066ab91e44405706233f2dfffee9add72 Merge branch 'trait-optional' of github.com:RainWarrior/FML + MinecraftForge/FML@32bb7315cc6beff84f186a33e73219cc5280821a Add in example assets dir. Closes #308 + MinecraftForge/FML@16d33d298953b41dbbe3e3b504e800f4f46a3e1b Clean up and document outbound handler a bit better. Add in dispatcher target. Closes #361 + MinecraftForge/FML@5719b9ec533b3e43213dbafcb448221884efd9e8 Fix reply handling. Make the proxy message available for subclasses of indexedcodec. Fire user events into the network event firing. + +Build 1.7.2-10.12.0.1007: + LexManos: Add support in Techne models for the TextureSize tag. Closes #856 + +Build 1.7.2-10.12.0.1006: + LexManos: Updated FML: MinecraftForge/FML@444a7d7fa1cf7fad7dda67f581fa0e3be36069b7 Move placement of single player world load hook to fix NPEs. + LexManos: Fixed RenderWorldLastEvent never being called, was missed in 1.7 update. Closes #932 + +Build 1.7.2-10.12.0.1005: + LexManos: Fix flexible rails, Closes #944 + +Build 1.7.2-10.12.0.1004: + LexManos: Fix warnings in Forge codebase. + LexManos: + Updated FML: + MinecraftForge/FML@7c5d62704ac1d3e586f3bfe26265a534e5362c73 Make UniqueIdentifier final and add a hashCode. Closes #348 + MinecraftForge/FML@ff7b5845e7f6b300d413b917f57adc472a4ebcff Clean up some warnings about @Override + MinecraftForge/FML@275ccac6f14bc66b88c76b1040aa7167f995967c Fix NPE at startup + MinecraftForge/FML@2a5a8d0cd062d3feac9c4de234e3dab1ff4462e5 Fix memory leak?! + +Build 1.7.2-10.12.0.1003: + Christian: + Add exception logging to forge channel handlers as well + + MinecraftForge/FML@53557dcd0582e09f7f35eb3bc2fd130fba3be4a0 Put logging exception handlers on all channel inbounds. Fix problem with failure to login. Closes #350 + +Build 1.7.2-10.12.0.1002: + Christian: MinecraftForge/FML@9c96ca4402e4c231285f170281dd543bfffa191a Fire a custom packet channel registration/deregistration event, for any mods that care about that kind of thing + +Build 1.7.2-10.12.0.1001: + Christian: + MinecraftForge/FML@e14efe786f6255a18e148c4137f560f5e2d2a38f Some fixes and tweaks + MinecraftForge/FML@c013870b1df5e63bd84d92545ebdd434db74b5d1 Merge branch 'simplenet' + MinecraftForge/FML@30882b0c1d2743afebbebc288d73f25696e0815c Clean up some warnings. Add in simple network impl + MinecraftForge/FML@9cab2ab36e7981c847e3e9ae8c3fbbb36531ba6d Add in some tests and examples for the "simple" network stuff + MinecraftForge/FML@a429e106dd00b34302ec5893e0a8fc97c8fc8019 Fix bug with SSP, and hook so we can do confirmation of world loading, as well as other things + +Build 1.7.2-10.12.0.1000: + Christian: + MinecraftForge/FML@b362e8a2733eb3082975edfdf83c996f048b65d3 At the request of AbrarSyed. + MinecraftForge/FML@e344303ec7a5ed27c4378ff072a036df7a350902 Merge pull request #346 from Jezzadabomb338/master + MinecraftForge/FML@a4686b1261a9bad523b4efa8a36a4433a58897cc Added basic Mojang account authentication support for development time login. + MinecraftForge/FML@dd17979a2f6f02ac4a9dda09b52c96365cc5fec9 Fix bukkit connectivity issue. + +Build 1.7.2-10.12.0.999: + Christian: + Updated FML: + MinecraftForge/FML@1db3daa0e82e67fc27ca3d535a09c806c1a54d67 added override toString method for getting full name. + MinecraftForge/FML@acf74a34032224a73c4c03280cafa0042c35cf5a changed the readme to reflect new setup task. + MinecraftForge/FML@96c19b35807fa078cb18b4ae50567d0360bcdb03 undid readme change + MinecraftForge/FML@a89939e57e9ff061df3d53cf1cb075b31de5de1b Merge pull request #336 from jadar/master + MinecraftForge/FML@717a8d694532bd9438eed8d9cf4b57318b2b4cfd Fix csv string vs list of strings. Thanks immibis. Closes #334 + MinecraftForge/FML@584c0f368bca1d5b0223b5b3611b366b9a00f7d7 Fix potential ordering issue, clean up some imports + MinecraftForge/FML@28293b29ea65c30fe80c49e85e2ae15a4db68933 Add in a simple(ish) event driven network handling system. Register using newEventDrivenChannel and you'll get a simple network handler that will fire events at the subscriber(s) of your choice, whenever a packet is received. You'll also get some convenience methods for sending to things. + MinecraftForge/FML@80b00dc7966d96111e2ce8643db8e0f544c2bc89 Fix openGui. Closes #342 + MinecraftForge/FML@fc69bcf2807dc2b85eb52681ba9531cb3e2f1945 Fix up privacy derp in TickEvent. Closes #343 + MinecraftForge/FML@10d056a494aac22137b644cff341a5958e8168fc Fix possible NPE derp + MinecraftForge/FML@5da6dcc7e3607e5f107f6a7d39a4b4e1eb7fb306 Divert connection through FML, so we can deny connections to servers that don't want us + +Build 1.7.2-10.12.0.998: + ohai.iChun: Squash commits so Lex would stop whining. Fix erroneous position when getting player position with changed eye height. + +Build 1.7.2-10.12.0.997: + Christian: + Updated FML: + MinecraftForge/FML@d5bfd69e35b21f701390a8c4c4c58d7ec1fff1fc Fix problem with connecting to vanilla. SHOW what's modded and what's vanilla in the list. Hooks that make the blocking work to come + MinecraftForge/FML@dd098854b0b65b8509b8788422e02d989a991b87 Fix the keybinding array to the right one + MinecraftForge/FML@43068eb9862f280611f26f4107ff5ac2b42b08e4 Fix TargetPoint to be static + +Build 1.7.2-10.12.0.996: + LexManos: Attempt to prevent a NPE when MC renders a lot of things at once. + +Build 1.7.2-10.12.0.995: + minalien: Fixed MinecraftForgeClient for custom Item Renderer implementations. Removed check for forward-slashes (/) in texture asset locations (but left check for backslash in place). + +Build 1.7.2-10.12.0.994: + LexManos: MinecraftForge/FML@0d810c01fab99ac491c2277097a4198518fe6c75 Mark jopt needed on the server, herp derp, blame Abrar! + +Build 1.7.2-10.12.0.993: + LexManos: Deprecate BlockFire.func_149842_a, and throw exception if someone tries to set the burn properties for air. Should prevent 'The Air is on fire!' reports. + LexManos: Fix DoublePlant placement, closes #921 + +Build 1.7.2-10.12.0.991: + verybigbro: Create ru_RU.lang + +Build 1.7.2-10.12.0.990: + Christian: + Updated FML: + MinecraftForge/FML@5317672631f30e1c9655f0bb28dd8b158deea2fb Add a utility method for finding the channel handler name based on type. Should fix naming weirdnesses. + MinecraftForge/FML@9de9a1553086ebeeb5d5fc0f6d96da8680e52df0 Fix stupid hardcoding derp + +Build 1.7.2-10.12.0.989: + Vexatos: Create de_DE.lang + Vexatos: Update de_DE.lang + LexManos: Fixed pipeline naming issue in the ForgeNetworkHandler. + +Build 1.7.2-10.12.0.987: + LexManos: Fixing an infinite recursion case, Closes #916 + LexManos: + Models now load from resource packs + Models must now be loaded from resource packs using the standard resource pack reference. For example, to load a model named "assets/mymod/models/mymodel.obj", you would call AdvancedModelLoader.loadModel("mymod:models/mymodel.obj"); + Closes #670 + LexManos: Fix panes/iron bars not connecting correctly. Closes #904 + LexManos: Fixed the run config for dev time server, Cloases #913 + +Build 1.7.2-10.12.0.986: + Adubbz: Made canBeReplacedByLeaves default to whether a block isn't opaque rather than if it is, also uninverted the checks for canBeReplacedByLeaves in WorldGenBigMushroom, WorldGenSwamp, WorldGenTaiga1 and WorldGenTaiga2 + +Build 1.7.2-10.12.0.985: + Christian: Fix inversion + +Build 1.7.2-10.12.0.984: + Christian: + Updated FML: + MinecraftForge/FML@21b13d63512ce399c82cbb6b9042eefa6dcdaacd Lots of network cleanup. Gui packets now work too! + MinecraftForge/FML@b3f98d1ee0416aa452f8611d458968afdf50775a Fix derpiness with Mods button when Realms is available + MinecraftForge/FML@cdd9d92a4f8cd199e2d8a34bb398ef32e5f1e275 Starting work on the actual GUI. Still work to do. Needs an API. + MinecraftForge/FML@ab5eb3ccfff7f9ccfd8720b23fcef3131e54d57d Fix button size for GuiModList + MinecraftForge/FML@3113138bd1377d71afe3b8290e18511bfb6e5e97 Tweak button positions. + MinecraftForge/FML@c5e29b574a315d48668ebc9189bcc497a0eae13e Avoiding redundant calls to LogManager + MinecraftForge/FML@fbc1f8f6f9effa4a538880f9fec0ce5010226d09 Config GUIs in the modlist now work. + MinecraftForge/FML@fa4f3015a0d7147cbde3edec7664e78e5bcacbb9 Added transparent background for GuiIngameModOptions as per cpw's request. May not compile due to manual de-mcp-fication. + MinecraftForge/FML@7bf119e1e54cadff690ec31a4bab93c0d1d0aad1 Fix up readmes and credits. We no longer support modloader. + MinecraftForge/FML@779cd05aa1ced720a63cc508b82e68cc6fc8daa9 Tweaks + MinecraftForge/FML@b51fb913551a5116cc3b9bb7583b1666f280c650 Merge branch 'patch-1' of github.com:airbreather/FML + Christian: + Updated FML: + MinecraftForge/FML@6f1da6550e10164bd6c678829f111bb5de9383b6 Fix up mcpname derp in GuiScrollingList. + +Build 1.7.2-10.12.0.982: + Adubbz: Fixed sky colour transitions on a render distance of 16, fixed the WorldGen of various things + +Build 1.7.2-10.12.0.981: + jk-5: Update ForgeMessage.java + jk-5: Added a constructor and made the fields package-private + jk-5: Added default constructor for reflection + +Build 1.7.2-10.12.0.980: + LexManos: Update patches for AT changes. + LexManos: Fix inverted login in BlockPistonBase causing blocks to break incorrectly. Closes #910 #909 + +Build 1.7.2-10.12.0.979: + Christian: + Updated FML: + MinecraftForge/FML@22ba6fda5ee2dbf29dc03ba93ff9c7707edeaeee Expose the nethandler in a few places, and pull out FMLEmbeddedChannel, exposing a utility method on it. More to come. + LexManos: Update for the FMLEmbeddedChannel change. + +Build 1.7.2-10.12.0.977: + Christian: + Fix build.gradle for tweakClass property + + Updated FML: + MinecraftForge/FML@f36152398d1d287e7a55a31c77a2614cfb63e1b6 Add in the tweakclass, fix the json for lzma @ the server. + +Build 1.7.2-10.12.0.976: + Christian: + Updated FML: + MinecraftForge/FML@5d069629cf47cd04f2002b3b9a2c32b0ea73c26e Allow itemstacks for furnace recipe inputs. Allow passing extra arguments through registerblock into the itemblock constructor. + +Build 1.7.2-10.12.0.975: + Christian: + Updated FML: + MinecraftForge/FML@061288909de0f0452adf51a5a9935fd09992c801 Fire simple network connect/disconnect gameevents. + +Build 1.7.2-10.12.0.974: + Adubbz: Made BlockCrops.getDrops call its super method + +Build 1.7.2-10.12.0.973: + Christian: Fix the network handler for forge + +Build 1.7.2-10.12.0.972: + Christian: + Updated FML: + MinecraftForge/FML@34819c9303870f560232464a2d16eb46d152515c Make gradlew executable on linux + MinecraftForge/FML@53a1f9841421b41d543d7d1d51319b44c86a527e Attempt to load old pre-1.7 worlds. ENSURE YOU HAVE A BACKUP! + MinecraftForge/FML@6a5f9e135f88b662e4e01e8882f861448910ca90 Fix example mod code for 1.7 + +Build 1.7.2-10.12.0.971: + Christian: Allow tools to override their material harvest levels. + +Build 1.7.2-10.12.0.970: + Christian: Tidier implementation of previous commit. Should fix for subclasses of individual tools too + +Build 1.7.2-10.12.0.969: + Christian: Add in harvesting abilities of the items, should fix effectiveness + +Build 1.7.2-10.12.0.968: + Christian: + Updated FML: + MinecraftForge/FML@f8d6213829d570501166d64d7c8bb4977567131f Update render registry + MinecraftForge/FML@ec316f113fefef12f6defed9eb68de368d7f4420 AT for renderblocks + +Build 1.7.2-10.12.0.967: + LexManos: + MinecraftForge/FML@3714426e19f8f0edaaeda8c787993f8f3615a44d fix derp in example builscript + MinecraftForge/FML@01fb451b6918599de5e732d7ff30c761438ab930 Merge pull request #311 from AbrarSyed/patch-3 + MinecraftForge/FML@30d532f4fc6fc65ea7e79707a75ff4d6ea0ea031 Pass 1: Most patches are restored. + MinecraftForge/FML@53127eec308d3929d68d3d9fafabcfef37e95c37 Merge branch 'master' of github.com:MinecraftForge/FML + MinecraftForge/FML@7ab3c3a37ceb8ab945208206aec86739a2138329 Update gitignore + MinecraftForge/FML@38cec7a11fae7cf12bda3a8d16a50bb6136d8886 The basic network handshaking for FML is done. All scenarios seem to work. + MinecraftForge/FML@9f928963f20bc9bbfbe1391fb16c6f5ca5fd4344 A network design I like. It uses the netty embedded channel to allow mods to build channel pipelines on top of custom payload packets. + MinecraftForge/FML@58f7487cfaf4a25a8349021b9cca5ef4ba0b541c Check in patches. Add in some network timeout tweaking values so you can debug the network. + MinecraftForge/FML@e544adba5c7e9286f917342af2669e5888fa0a17 More cleanup. Bidirectional server <-> client network works for mods as well as FML|HS now. + MinecraftForge/FML@f5c38e2359c2e6eca13cd6606465ee36086a7113 OK, network channels appear to be working well for both dedi and integrated servers. The beginnings of the new mod structure too. + MinecraftForge/FML@9c96a0a10a5cbe34786be8fd41f9818b5ac929bc More network stuff, partially done id syncing. + MinecraftForge/FML@2aaaeba15eabdec189daa8662e9ffdf0b5a09dbe ID loading from the server save now works. + LexManos: Kill liquids finally. + LexManos: Bump data for 1.7 start. + LexManos: Some work on 1.7, waiting for Abrar to fix a few things. + LexManos: + Updated FML: + MinecraftForge/FML@a30f17362764f3e4e594386e193f9e4368e6836e Add sonatype snapshots repo for SpecialSource snapshots. + MinecraftForge/FML@b2550b8a693315ccc205f5315eac67c5283d7af9 Add mappings for Items/Blocks fields. + MinecraftForge/FML@57f7f1d7abd304d3e9f42567f1d66c10122e4ec6 Update for fixed Enum cleaning and names. + MinecraftForge/FML@99c681ad8736e4976053718c3d453b2fb30eefe0 Did it manually, forgot it needed the end comma + LexManos: + Updated FML: + MinecraftForge/FML@1d71c017f45aa7ed9d7d7c5ed5250a8d22477980 ID syncing. + MinecraftForge/FML@fd36f50d8210342f65cb0272bac56a3bcc42dd18 Cleanups + MinecraftForge/FML@89e4e483c204c11b6fdfed34893fc223a7d6a899 Add in a remapping event for mods to consume. + MinecraftForge/FML@cd417c6786256fa23f181ff0b76696bc6dfb0291 Fixed remote and local connections work with mods now. + MinecraftForge/FML@ef492407ef812bb6bbc7f0bd8efbd16d07efcafb Entity spawning works. + MinecraftForge/FML@d0d31d9575403eb2ec058898b86ffd99a9220f75 SpawnAdjustment packet. Clean up stuffs. No more compile errors. + MinecraftForge/FML@3e278acb71e4e3d0406e80f0fad5071c9215ed33 Fix stupid possible compiler error. + MinecraftForge/FML@009d4dee2328cc8d97b74177a2c5a3c359e6564a Fix the exc file. Fix the deobfremapper for handling inner classes. + MinecraftForge/FML@49cb893d12bd4f82b5d1b50d1e6517a256525d32 Fix exc this time *sigh* + MinecraftForge/FML@31efcfc3b2085f5d4e070ddab34a0be1481b4c6f Clean up patches for latest exc. + MinecraftForge/FML@7a4ceebf5efe5b3650080cf912e371d92fc70a55 Kill old patches and add rejects to ignore file. + MinecraftForge/FML@7ea571f593464ad4226ba845da27ff66161621b1 Fix AT, Fix exc. Almost works now! + MinecraftForge/FML@b852e302851cfaf77e1db6f86408e8d049703656 First functional release under reobfuscation. + MinecraftForge/FML@df870c1a3341d8e2e88d7fc3e2f3d9ed2507989f Update shiped ForgeGradle for 1.7 + MinecraftForge/FML@55aa337f952bc72c5a001a6ed661978b11822c63 Rename synthetic bridge methods, these methods are not decompiled, but need to reobf correctly cross the recompile boundary. + MinecraftForge/FML@0098c57f94808751062ee45f2ee267324bb42089 Merge pull request #316 from AbrarSyed/patch-4 + LexManos: Comment out fluids until King gets his hands on it + LexManos: Killed a few hundred compile errors. + Christian: + Moved the core event handler parts to FML. Implemented the Forge network handler based on the new + netty strategy. + + Updated FML: + MinecraftForge/FML@3b2994a3def35a2d3058960b71dc59dc48b802f9 Some patching touchups + MinecraftForge/FML@557357fe179529e0b44aab2f3fcef0c5adf981d5 Update for log4j2 + MinecraftForge/FML@a2b324beb2ef6ec73000678c9305fd70d4ec1643 Copy eventhandler from Forge into FML. It is going to replace a lot of the runtime event systems. + MinecraftForge/FML@4071ff38afe15fddf5db0be882f5627f503a37c6 Patch some GUIs. We're gonna add some basic mod gui config support. + MinecraftForge/FML@0b419ac79c307579f162d47e0388a9d75bcd0a6e Fire a user event down the channels when a handshake has occured. + Christian: + Updated FML: + MinecraftForge/FML@32561265fc935cd6639d5b2e086e879f375676fa Ticks, Player events, keybindings, all migrated to the new event driven system. + LexManos: Inital patch update for 1.7.2, Doesn't compile, not done yet. + LexManos: + Updated FML: + MinecraftForge/FML@a17489172cd54ca955548b15fa0669c9f95d7f45 Code to disable mods at runtime, or other times. + MinecraftForge/FML@85516d9588ebfadbba25f21b2f973e4e81abbaa6 ICraftingHandler, IPickupHandler are now both events. + MinecraftForge/FML@e4b63a1801b453797f5e820eb3f5bd42e6d43948 IWorldGenerator now has an ordering at registration time. This means the order of worldgen should be much more stable. + MinecraftForge/FML@ef3856f9a34e82a05cb2b7715e3611f8fb1a9a6b Update patches for Gradle's rename rewrite: MinecraftForge/ForgeGradle@19e7acf2a27a6c6ae60f6e8ab38337defddc16d3 + MinecraftForge/FML@31ea100b29dfdb4fc907e212c3d49a5240ca72a9 Working on id missing handling + MinecraftForge/FML@3e76dfba34aaba4397fc3fb2bd28e0d1f0abe3e4 More tweaking on id stuffs at worldload + MinecraftForge/FML@f860c8ad3bc7537f885b27c7f045b5b1140c05f4 bump to legacy launcher 1.9 with logging unification stuffs. + bloodshot: + Refactored BiomeManager stronghold add/remove methods to support new + + MapGenStronghold dynamic biome changes. + Changed InitNoiseGensEvent to pass a NoiseGenerator array instead of + NoiseGeneratorOctaves due to new NoiseGeneratorPerlin in + ChunkProviderGenerate. + Fixed worldgen crash caused by wrong metadata in Chunk patch. + LexManos: Get object based on identity not name. + LexManos: Forge uses the FORGE channel for packets. + LexManos: Fix MethodNotFound crash when shutting down internal server. + LexManos: + Fix: + Block placement/interaction + Tesselator crash due to wrong mapping + Missing isreplaceable check in world.canPlaceAt + Small foratting/logging cleanup + Temporarly commented out efficancy changes till I implement it. + LexManos: + Updated FML: + MinecraftForge/FML@58132ccda3a575f10fc209c421fd5d80e01164cc Add new required --accessToken to launch specs. + LexManos: Bump version to 10.12.0 to mark 1.7. + LexManos: Fix debug HUD rendering semi-transparently. + LexManos: Temporary fix for items rendering with effects on one layer. Restores default vanilla rendering, which is considered a bug in modded community. + LexManos: Move Grass {Flower} registry to BiomeGenBase as 1.7 made flowers Biome specifc, this means modders who wish to add global base flowers need to add them to all the biomes indavidually. + LexManos: Fixed accedential inverted logic that caused Potions to render incorrectly. Thanks iChun. + LexManos: Fix items rendering on the GUI with invalid state due to glint rendering changing it. Thanks iChun. + Christian: + Updated FML: + MinecraftForge/FML@156a9ae03a3c80bd1499a8e692c44a322ad9df62 Fix build script + MinecraftForge/FML@7d3b1250e8368886bed0e0da350a94abaa4b6247 Fix handshaking properly. Also, fix ID syncup. It now will completely freeze the idmap after preinit. This frozen map is used to inject "new" stuff into existing serverside worlds. Interesting sideeffect: remote servers lacking things will change the client, to also LACK ids for those things. watch for -1 ids in your remap events. Note: idmaps should be considered temporary per server instance. SERVERSTOPPED will restore "startup" state, as will client logout. + LexManos: + MinecraftForge/FML@c180d9b15735ce89a38c497acd65fa3fab595f77 Add 1.7.2 base json, Gradle will automatically download updated versions of this. Keeping it in the repo allows us to se + e what base json we have built our jsons off of. And if it changes update accordingly. + LexManos: Added constructor to WorldType, and delegated the BiomeLayer management to it. Also updated the access transformer for Item/Block classes. + LexManos: Forgot to regen patches... + LexManos: Resize pending tile updates when it goes over the curent length. Fixed AIOOB error. + LexManos: Add a version check mechanic to startup, it is done in a seperate thread and has a config option to compleetly disable it. This allows us to notify users of new recomended builds. Hopefully stemming the flow of outdated help request. Also adds a warning to the main screen if you are running a 'Beta' Forge. Which means a Forge for a new version of Minecraft that we have not promoted a recomended build for yet. + LexManos: Fix dig speed on redstone ore and obsidian. + ohai.iChun: Add render offset to event. + Christian: + Updated FML: + MinecraftForge/FML@268bbabee6ae3fa1d596bd18e172298e26dc9ce4 Fix handling world reloads when new stuffs are added + +Build 1.6.4-9.11.1.964: + LexManos: + Bump build for gradle fixes: + Now Builds for java 1.6 + Include version,json in universal + Fixed classpath issues in eclipse task. + +Build 1.6.4-9.11.1.963: + luacs1998: + Update readme for ForgeGradle + + Let me know if there's anything else to add or change. + luacs1998: Updated + luacs1998: Another update for eclipse users + luacs1998: Update README.txt + +Build 1.6.4-9.11.1.961: + LexManos: Updated FML: MinecraftForge/FML@c2b919d339e5f63271cfb67a77235c21c5c3b80e Don't validate signatures in dev env. + +Build 960: + Abrar Syed: Step 1: The Purge + Abrar Syed: update FML for gradle changes + Abrar Syed: Step 2: The Reformation + Abrar Syed: Step 3: The Rebirth + Abrar Syed: fixed versioning and stuff + Abrar Syed: added buildSrc to the gitIgnore + LexManos: Fix for new FML, and publish to maven local + LexManos: Update run configs. + LexManos: + Updated FML: + MinecraftForge/FML@e9a7660cb8961660186c7c23e61ab35f9c2dfb81 updated samples + MinecraftForge/FML@30894f7afadf5d3f3c3d5a54c3f904413d5f2309 Merge pull request #307 from AbrarSyed/master + MinecraftForge/FML@c4b8a393f90b00ad7ee4992ea4341ffb6d676abb Make the working directory the root of the eclipse workspace. + LexManos: Regenerate all patches, No functional change as added this is a formatting change only. + Abrar Syed: update build.gradle + porcariadagata: Make the gradle wrapper executable. + LexManos: Proper configuratuion name for jenkins. + LexManos: Fix build file pom closures. + +Build 1.6.4-9.11.1.953: + LexManos: Fix NPE on specific tile entities when the block break event is canceled. Closes #863 + LexManos: Fix NPE in FakePlayers when they are created in a purely client side environment. To remove in 1.7 as FakePlayers should be used for server side interaction with the world on a player's behalf, not for client rendering. + +Build 1.6.4-9.11.1.952: + Christian: + Updated FML: + MinecraftForge/FML@23baf3a8ce58cb8306189401a60647957ccbb4c2 Actually fix the nethandler code + + Update patches + +Build 1.6.4-9.11.1.951: + Christian: + Updated FML: + MinecraftForge/FML@b7f34629c3c47b92ee89d72b0dc935b4997cb009 Don't try and open GUIs on the server. + Christian: + Updated FML: + MinecraftForge/FML@da72640c7ef1f44c49f7f592fbdd193622a30b40 Way to go, missing import. *sigh* + +Build 1.6.4-9.11.1.949: + Christian: + Updated FML: + MinecraftForge/FML@6af42bc656dfb98972d034363352affc9e777805 Add in null protection for client/server sides of handleChat + Christian: Don't send openGui commands from the fakeplayer. Fixes thaumcraft and probably others. + +Build 1.6.4-9.11.1.948: + CovertJaguar: Fix Water/Lava Fluid Localization + +Build 1.6.4-9.11.1.947: + LexManos: Noop out FakePlayer.addStat. + +Build 1.6.4-9.11.1.946: + Christian: + Updated FML: + MinecraftForge/FML@3d25b4e793c59a9131a441d6c7a2d80cac9cd701 Add in the ability to strip interface references for specific interfaces - this is probably mostly useful for scala scenarios where sythetic methods are generated and is not a substitute for using Optional.Method where appropriate. Closes #300 + +Build 1.6.4-9.11.1.945: + onibait: Add block break events based on @bloodmc's initial 1.5.2 Pull Request + onibait: Add block break events based on @bloodmc's initial 1.5.2 Pull Request + onibait: Fixed formatting + onibait: + Cleaned up patches to BlockOre and BlockRedstoneOre + Fixed trailing whitespace (it bugs me too) + +Build 1.6.4-9.11.1.944: + Christian: + Updated FML: + MinecraftForge/FML@f4532410ec1dbf43ce15dfa78d07e5f7be408b08 Change a couple of warnings, as a prelude to 1.7- preinit is now required for all GameRegistry activity, and every item and block REQUIRES registration. + +Build 1.6.4-9.11.1.943: + Christian: + Updated FML: + MinecraftForge/FML@8f87021b0f1ae5b277ad4d1891761b7a7ae1ab71 Fix derp with custom properties. They work now! + +Build 1.6.4-9.11.1.942: + Christian: + Updated FML: + MinecraftForge/FML@bc57ff9e83803d804e9d5374d76273fcd68611f4 Fix recursive API dependency resolution. Allows nested APIs, such as BuildCraft's + +Build 1.6.4-9.11.1.941: + Christian: + Updated FML: + MinecraftForge/FML@de8ab934d8ae960ebc0dede16218ca1e9e488ebc Fix up duplicate entries + +Build 1.6.4-9.11.1.940: + Christian: + Updated FML: + MinecraftForge/FML@81fe1c9682234297443402a54e4b852ef49d0ba8 Add in an API marker for API type packages. This does several things: 1. Packages marked as API will generate a new "modid" (the provides) that can be depended on. 2. Packages marked as API will be searched systemwide, and anything declaring that package (even without the API marker) will get an implicit dependency on the API package. 3. The API package itself will get a soft dependency on the "owner" package. + +Build 1.6.4-9.11.1.939: + LexManos: Add new recipe sorter that is called after all mods are initalized. This is disabled by default in 1.6 to not break current worlds as it may change machine's recipy outputs. Will enable by default in 1.7. Players may enable it in the forge config. + +Build 1.6.4-9.11.1.938: + xcompwiz: + Adds a Check to prevent Biome Replacement + + Splits the BiomeGenBase constructor to create one which takes a flag + that indicates whether to insert the biome object into the biomeList + array. The standard constructor calls the new one with the default of + true. This allows biome wrapper-objects to exist. + +Build 1.6.4-9.11.1.937: + Christian: + Updated FML: + MinecraftForge/FML@dac7f590eabb326c4467dbc829b4aae1e4be2779 Modify ordering of networkmod registration and mod instantiation. This fixes VersionCheckHandler logging an incorrect failure message due to NPE. + MinecraftForge/FML@f0dc530b2833a1c89673208fe296dba5520671c1 Fix up documentation of VersionCheckHandler - it only ever accepted a String and only works on the NetworkMod annotated class + MinecraftForge/FML@243a21a353e6b7717f64008776928c7132110ddf Wrapping coremods as tweakers. Part 1. + MinecraftForge/FML@58a299aabcfadb4139f126a2d46b5247bede4185 Attempt to inject coremods as tweakers, so both can share a dependency ordering + LexManos: Format strings properly in MC's internal logger. Fixes resource pack case warnings. + +Build 1.6.4-9.11.1.935: + LexManos: Updated FML: MinecraftForge/FML@bf54d4d66799f2e58944095826d0722ed0120b1f Make each mod's EventBus log a child of it's main logger. + LexManos: Fix null pointer exception in BiomeDictionary causing the ChunkManager's config to not load/save. + +Build 1.6.4-9.11.1.934: + LexManos: Add wildcard versions of OreDict replacements, Closes #827 + +Build 1.6.4-9.11.1.933: + Christian: + Updated FML: + MinecraftForge/FML@da4337efbfa07b35f5883107768f9ba2f1b24b9b Fix up handling the new method signature data for NetworkCheckHandlers + +Build 1.6.4-9.11.1.931: + Christian: + Updated FML: + MinecraftForge/FML@f92962bbbbb90c19788a5dc2eafdc2eeefdd77ce Use null to empty, so missing values work. *sigh* + +Build 1.6.4-9.11.1.930: + Christian: Add in an event for zombie summoning. Allows for mods to control summoning behaviour, as well as custom summoned mob. + Christian: And remember to make the event class static *sigh* + +Build 1.6.4-9.11.1.928: + Christian: And allow for tweaking baby chance as well. + Christian: And set the RIGHT variable *sigh* + +Build 1.6.4-9.11.1.926: + Christian: Allow configuration of the zombie additional summoning mechanic. The vanilla mechanic is a little borked at times. + +Build 1.6.4-9.11.1.925: + Christian: + Updated FML: + MinecraftForge/FML@58577775d277a4408bda510534eb36841b08ced4 Very minor style fixes + MinecraftForge/FML@96be82343c25b83dd842ada8d6e8b66eb4e4ee00 Merge pull request #280 from mc10/patch-1 + MinecraftForge/FML@2714da10228020a6f2321f6c9a703f0d24fe1370 Primitive capability for tweakers to order. Add a "TweakOrder" integer property to your manifest, or accept the default of zero. + +Build 1.6.4-9.11.1.924: + LexManos: Prevent session from being printed to the console, also make sure the username is not empty. Causes a lot of bugs further down the line. + +Build 1.6.4-9.11.1.923: + Christian: + Updated FML: + MinecraftForge/FML@fc3e7647d2aff01146b1f5bd2ab6b57ef8e833e5 Support, and fix up, interface lists for Optional + +Build 1.6.4-9.11.1.922: + LexManos: Added some missing air checks to world gen features. + +Build 1.6.4-9.11.1.921: + LexManos: Fix repeated argument in CleintCommands. + +Build 1.6.4-9.11.1.920: + LexManos: MinecraftForge/FML@a381874bb9c3bdeeb508bb81719b4d210eb29696 Delay sound system backend initalization to speed up startup and prevent race condition on some computers. + +Build 1.6.4-9.11.1.919: + reflex_ion: + Added PlayerOpenContainerEvent and added ForgeHooks.canInteractWith + + - Used to override the canInteractWith during player tick + - setResult to ALLOW/DENY as required + - Defaults to Vanilla behaviour in any other instance. + + Required for LittleBlocks Mod and to Assist Gullivers Mod + +Build 1.6.4-9.11.1.918: + Christian: + Updated FML: + MinecraftForge/FML@bc64ceabef76b1f4667b22ca8241b72351b44338 Optional shouldn't be constructable itself. It's purely a wrapper thing. + MinecraftForge/FML@55525f6d2eb24f42c26a291b8ce98feb4d4498c9 ModLoader is officially deprecated. It will all cease to be with 1.7. + +Build 1.6.4-9.11.1.917: + Christian: + Updated FML: + MinecraftForge/FML@63ba3aa0099f43183315fb4e16f9e8e8007362f8 Add in support for Optional interfaces and methods. Be gone coremods! + +Build 1.6.4-9.11.1.916: + LexManos: Fix missing patch which caused per-world storage to not be saved. + +Build 1.6.4-9.11.1.915: + rhilenova: Added pre/post to player list rendering in GuiIngameForge. + rhilenova: Moved PLAYER_LIST event inside display check. + +Build 1.6.4-9.11.1.914: + LexManos: + Make isItemStackDamageable() pass the stack to getMaxDamage() to use the Forge version of getMaxDamage(). + Fixes display issues with mods using getMaxDamage(ItemStack) instead of the vanilla one. Closes #805 + LexManos: Bump version number for todays changes. + +Build 1.6.4-9.11.0.913: + ben.blank: allow blocks to choose how they handle indirect power + ben.blank: move `shouldCheckWeakPower` to Forge section + +Build 1.6.4-9.11.0.912: + jrtc27: Pass arguments to install.sh and install.cmd to install.py + +Build 1.6.4-9.11.0.911: + LexManos: Add cancelable EntityStructByLightningEvent, Closes #789 + +Build 1.6.4-9.11.0.910: + LexManos: Stack sensitive version of Item.getItemStackLimit. Closes #771 + +Build 1.6.4-9.11.0.909: + Anthony Lomeli: Villager Trading GUI will not open if Player is sneaking. + +Build 1.6.4-9.11.0.908: + LexManos: Added all the vanilla records to the ore dictionary. Closes #731 + +Build 1.6.4-9.11.0.907: + LexManos: Direct canApplyAtEnchantingTable through canApply Closes #740 + +Build 1.6.4-9.11.0.906: + LexManos: Add pre and post event to rendering Chat, allowing for placement of the chat box. Closes #733 + +Build 1.6.4-9.11.0.905: + LexManos: Set densityDir in BlockFluidBase's constructor, closes #737 + +Build 1.6.4-9.11.0.904: + LexManos: Fix placement of snow cover over metadata 6. Closes #724 + +Build 1.6.4-9.11.0.903: + LexManos: + Add the ability to register chat commands that only execute on the client. Works with autocomplete. + Client commands are gray when shown in the autocomplete list (when you press tab) + Closes #640 + +Build 1.6.4-9.11.0.902: + LexManos: Update workspace to point at launchwrapper 1.8 + LexManos: Vanilla hopper should obey the IInventory contract, TileEntityHopper now takes into account IInventory.getInventoryStackLimit() when inserting items Closes #597. + +Build 1.6.4-9.11.0.901: + hobos_taco: + Added ItemTooltipEvent + + This event is fired at the end of ItemStack.getTooltip(EntityPlayer, boolean), which in turn is called from it's respective GUIContainer. It allows an itemstack's tooltip to be changed depending on the player, itemstack or whether the advanced information on item tooltips is being shown, toggled by F3+H. + +Build 1.6.4-9.11.0.900: + Christian: + Updated FML: + MinecraftForge/FML@5265e34a350adbb762264379f0134bfa40d33eaa Fix null killing the server + +Build 1.6.4-9.11.0.899: + LexManos: MinecraftForge/FML@35ab9f52b02d84592e4c7607feb6009710b2f7d9 Fix md5s for new checksums.sha1 in scala libraries. + +Build 1.6.4-9.11.0.898: + Christian: + Updated FML: + MinecraftForge/FML@e356f4d713b1269825839954fe86f5312ede0fc6 Cross-modsystem compatibility fix with thanks to Mumfrey @ liteloader + MinecraftForge/FML@9b55f1f48f89a5348ac1d58622b71946f310316a Attempt at a shared modlist implementation - should allow visibility between liteloader and fml for "mod list display" + +Build 1.6.4-9.11.0.897: + Christian: + Updated FML: + MinecraftForge/FML@bfc25bc5da1ff0f6fd0faf817b32a8f6d35dedc2 Add to both the classloader and the parent + +Build 1.6.4-9.11.0.896: + Christian: + Updated FML: + MinecraftForge/FML@9f0f9e7288afc6cce9a425ad770a208af9e28648 Fix deobf tweaker + +Build 1.6.4-9.11.0.895: + Christian: + Updated FML: + MinecraftForge/FML@ce6404fd5bb5e8e425af3bcafeaa285575bf39a3 TYPOS!!! + +Build 1.6.4-9.11.0.894: + Christian: + Updated FML: + MinecraftForge/FML@8f18a3de9a02b003762dace891829ef64dfedf49 Separate deobf tweaker so it runs last. Should fix problems with cascaded tweakers expecting an obf environment. + +Build 1.6.4-9.11.0.893: + Christian: + Updated FML: + MinecraftForge/FML@8e26c99de3b44d272d2fdc398e0687db17bce3b7 Add debugging for deobfuscation + MinecraftForge/FML@1d902df5814b815959165e4aa69272003f002d25 Use the negative cache in latest legacylauncher. + MinecraftForge/FML@9815d8c3793182a08fcdbd29376a3f70bff464d0 Update for launchwrapper 1.8 + +Build 1.6.4-9.11.0.892: + LexManos: Add wrapper exception to the new Structure ID system to point to the correct Structure. + +Build 1.6.4-9.11.0.891: + LexManos: Update dev workspace for 1.6.4 .. + LexManos: Remove some side onlys on BiomeEvents that don't need them. + LexManos: Fixed typo in bounding box based ladder checks that caused potential infinite loops with entities in certian positions. Thanks Overmind for reporting it. + LexManos: + Updated FML: + MinecraftForge/FML@fc8c3bef0380d59c0842a252e4f0bd29127ee78b Update to new installer that uses xz compression and better support for non-standard jvms + +Build 1.6.4-9.11.0.886: + Christian: + Updated FML: + MinecraftForge/FML@a4de22c1addf0a6b95d38e467a96f2af417c86d5 And use the parent that's actually going to be valid. *sigh* + +Build 1.6.4-9.11.0.885: + CovertJaguar: + Fluid Rarity should have a default + + Oops. + Christian: + Updated FML: + MinecraftForge/FML@70cffe6982b27df0ea7d8d4d8851a0c0043bb2cb Herpaderp. Make addURL accessible. *sigh* + +Build 1.6.4-9.11.0.884: + Christian: + Updated FML: + MinecraftForge/FML@4a94c2c71bb4cc9644caeb06011a189989b22f87 Fix NPE when loading second single-player world + MinecraftForge/FML@0e80fcb8f716cfef5b016a73ca32ff0e2f3c3c05 Merge pull request #284 from smcv/uninit + MinecraftForge/FML@39620f1e41464f53482277dc3bcb0b9eed8ca25c Fix injection of tweaker into system + +Build 1.6.4-9.11.0.883: + Christian: + Updated FML: + MinecraftForge/FML@79c39f8b7c711377f7919abd1ee6a56a339d1062 Fix classpath for launchwrapper 1.7 + +Build 1.6.4-9.11.0.882: + traincrazyb: Correcting SoundManager Transformer + +Build 1.6.4-9.11.0.881: + Christian: Clean patch fuzz + +Build 1.6.4-9.11.0.880: + Christian: + Updated FML: + MinecraftForge/FML@f6190e8752013c0d6857090ffd42559cf83809ae Update workspaces for 1.6.4 + +Build 1.6.4-9.11.0.879: + Christian: + Updated FML: + MinecraftForge/FML@25981706ef12654b6c2baccc80fa2298bb5afb4a Update for MC 1.6.4 + MinecraftForge/FML@0950b9fc8441a2d3b022ed876f17ee5ac0c47b9c Fix up a deep crash in the early server init hanging the client + +Build 1.6.3-9.11.0.878: + LexManos: Remove duplicate access transformer + LexManos: Move have handler creation up, to prevent NPE. + +Build 1.6.3-9.11.0.877: + LexManos: + Updated FML: + MinecraftForge/FML@e7dd728f955aa4713fef45fea770b1f91246d712 Format the log messages through MessageFormat. Thanks CovertJaguar for the pointer. + MinecraftForge/FML@82d896a35e08be5712bdc15bdb93e5d4fc0ddd46 Make final transformers actually work on methods as well. + +Build 1.6.3-9.11.0.876: + LexManos: Move the Structure data save files to per-world data folders. Vanilla saves them to the global folder which will potentially cause conflicts if two worlds generate 'Villages'. Which in the modded world is highly likely. Refer back to the long standing vanilla issue where nether 'villages' would override the overworlds villages, it's the same situation. + +Build 1.6.3-9.11.0.875: + LexManos: Make MapGenStructureIO name registration functions public, Any mod that has anything extending StructureStart or StructureComponent must register there classes and create a default (no parameter) constructor. + +Build 1.6.3-9.11.0.874: + LexManos: Unfinalized Item.getIconIndex(ItemStack) + +Build 1.6.3-9.11.0.873: + LexManos: + Updated FML: + MinecraftForge/FML@da70cdd35a378d02db47d2aa31fb9aac87beaedc Update tweaker for new Launcher API + MinecraftForge/FML@352117fd78eec745da6c80b8d354947c4dbbbb48 Update for new installer and thank you. + MinecraftForge/FML@40a34af43105ebcb5a63ce2351ca119da5a8158e Merge branch 'master' into newtweak + MinecraftForge/FML@e77d2547ad447025729ae7f3cccaaf343c4c86f9 Update for pre-ninja 1.6.3 update + MinecraftForge/FML@34493b0d99e2cf2bdca080ff226f2dfeedb1cf51 Update for real 1.6.3 update + MinecraftForge/FML@95afc95b248ecc69bc6ffcc5a95912820d8f4066 Update mcp mod info + MinecraftForge/FML@ff75416a325717770a5cf457c859bfb0abcbe281 Update mc_versions data and commands patch and eclipse workspace + MinecraftForge/FML@8f2e67558127f16d92399ea97cbcb0df46d52e19 Update the src distro's eclipse workspace. + LexManos: Bump version for new MC version. + LexManos: Actually push the submodule update -.- + +Build 1.6.2-9.10.1.871: + Christian: + Updated FML: + MinecraftForge/FML@81328b6684c5127427153807b5e498c2efefb96b Add in support for using a mirror list and provide checksums for packed download of libs + +Build 1.6.2-9.10.1.870: + Christian: + Revert "Added a RenderType that allows Map-style rendering w/o inheriting from" + + This reverts commit eb4688bf5ea132cd8ddc802a7dad6d423ad50543. + + Conflicts: + patches/minecraft/net/minecraft/client/renderer/ItemRenderer.java.patch + +Build 1.6.2-9.10.1.869: + vilim.lendvaj: Prevent NPE in fluid lookup for block + Christian: Fix broken PR from vilml. TEST! + +Build 1.6.2-9.10.1.867: + CovertJaguar: + Allow Fluids to have Rarities + + Used for tool tips. + +Build 1.6.2-9.10.1.866: + Christian: Fix the divider + +Build 1.6.2-9.10.1.865: + Adubbz: + Sky colours now smoothly transition + + Made transitions even smoother + + Sky colours now smoothly transition + Christian: Move stuff around a bit- also tie range and enabled to options. + +Build 1.6.2-9.10.1.864: + ml: + Added a RenderType that allows Map-style rendering w/o inheriting from + ItemMap. + Christian: Tweak patch comment + +Build 1.6.2-9.10.1.863: + mitchpetrie29: Check if block is air instead of just ID zero when growing from stem block. + +Build 1.6.2-9.10.1.862: + Christian: Allows proper raytracing from actual player eye position rather than hardcoded eye heights. + +Build 1.6.2-9.10.1.861: + claire.alexandria: Added fov hook + claire.alexandria: fixed merge + claire.alexandria: Small formatting changes (opening braces on new line) + +Build 1.6.2-9.10.1.860: + Christian: + Updated FML: + MinecraftForge/FML@b5af446d7111730c7973c9f0d6b76e62b78b9131 Fix sidedness derp in LanguageRegistry. + +Build 1.6.2-9.10.1.859: + Christian: The partial tick should be available for all render player events. Weird it wasn't. + +Build 1.6.2-9.10.1.858: + Christian: Reverting HarvestEvent, for new implementation + Christian: Redo harvest event. This time with simpler logic, that should be less liable to weird "missing" stuff. + +Build 1.6.2-9.10.1.857: + LexManos: Fix bug where guis were not closed properly, resulting in signs not having there text set. + +Build 1.6.2-9.10.1.856: + Christian: Change trigger calculation so entities get some time to age before refiring the event. + +Build 1.6.2-9.10.1.855: + LexManos: Create helper apply_patches script and add paramter to change patch output folder. + +Build 1.6.2-9.10.1.854: + Christian: + Change from Cancelable to using a Result. This means you can force despawn mobs you + don't want around anymore. Also, deferred check to once every 20 ticks. May tune it + down further or make it a config if this event is a lag issue. + +Build 1.6.2-9.10.1.853: + Christian: + Add a cancellable despawn event- allows mods to decide to prevent the despawning + of certain otherwise normally despawnable mobs. + Christian: + Added fov hook + + Small formatting changes (opening braces on new line) + Christian: Create es_ES.lang + +Build 1.6.2-9.10.1.852: + Christian: Add some javadoc to the HarvestBlock event. + +Build 1.6.2-9.10.1.851: + Christian: Use the dropchance from the event. + Christian: Clean up some formatting. + +Build 1.6.2-9.10.1.850: + claire.alexandria: Added tessellation methods to obj model, for ISBRH-friendliness + claire.alexandria: Fixed both RenderAllExcept behaviours + claire.alexandria: Added more render methods to IModelCustom interface. + jk-5: + Added GuiOpenEvent + + You can use this for a clean way to override guis, without needing an + TickHandler to check if the gui you want to override is open and show + your own gui + + Proper close check + mhahn: + capacity was not respected + + Updated to properly calculate the amount of free space in the tank + before checking that against the amount of the resource. + robin: create french localization + claire.alexandria: Added display name hook + claire.alexandria: Memoization of display name result + claire.alexandria: Added mouse event + claire.alexandria: inserted hook into Minecraft.java + claire.alexandria: fixed logic error + ohai.iChun: + Fix player push out of blocks clientside being hardcoded and not based on entity size. + Added player eyeHeight field to allow changeable eye heights of players rather than being hardcoded per player class as well as add a getDefaultEyeHeight function. + Christian: Reduce patch size significantly + Christian: Fix patch for new MCP naming. + Christian: Add a bit of javadoc + Christian: MachineMuse, remember, there's TWO model formats supported! + Christian: Defer firing CreateDecorator until it's likely mods have had a chance to register their listener. Should close #759 + Christian: + Add a harvestblock event, to allow mods to intercept and change the drops for blocks. Don't abuse this, or we'll have to take safety measures. + Fires for both silktouch and non-silktouch harvesting, and provides the player. Note, you may need to + change your break overrides to pass on the player for best results. + Christian: Add in a block reverse lookup for fluids. Closes #749 + +Build 1.6.2-9.10.1.849: + Christian: + Update forge for MCP naming updates + + Updated FML: + MinecraftForge/FML@d0c6e92900590f578b80d9a6c00fa28fd333d3bf Update MCP data + Christian: Update version to 9.10.1 for mcp naming changes. + +Build 1.6.2-9.10.0.848: + Christian: Fix possible NPE in searching code. + +Build 1.6.2-9.10.0.847: + Christian: + Updated FML: + MinecraftForge/FML@2a9c485edc4cf3382154d5b3b9b600386f2ab8ae Remove @SideOnly from 70318 (getDistance) it makes no sense that it's not on the server. + +Build 1.6.2-9.10.0.846: + Christian: + Updated FML: + MinecraftForge/FML@a13598b17ea9637c054d867a76298d6c080c5e32 Use java 1.6 compatible method of closing the zip file. Stops stupid compile error. + +Build 1.6.2-9.10.0.845: + Christian: Small fix to container registry. emptyContainer is not null, it's "NULL_EMPTYCONTAINER" now and won't match any valid container. + +Build 1.6.2-9.10.0.844: + Christian: Tweak the release to add in assets to the distributable. *sigh* + +Build 1.6.2-9.10.0.843: + Christian: + Updated FML: + MinecraftForge/FML@1c9a853868f7df0daa5f67b99401dfab44ae18e6 Allow coremods to properly inject asset readers. + MinecraftForge/FML@40b54013b4c9b01686411cd47a7866eeb650ea2b Allow server side lang file injection, hopefully + Christian: Add some starting work for a forge tps command. Also update coremod for new FML behaviour + +Build 1.6.2-9.10.0.842: + Christian: + Updated FML: + MinecraftForge/FML@b993cf4a9825865b3a8a0c7b083c23d56dbd1d6f More exception handling for less derpiness. + +Build 1.6.2-9.10.0.841: + Christian: + In the time honoured tradition of trying to fix vanilla, we today attempt to patch the pathfinding AI so that it doesn't lag when + there's a lot of entities. Basically, if the zombie can't reach the villager, backoff subsequent pathfinding attempts. Hopefully + should really help with lag caused by zombie swarms. + +Build 1.6.2-9.10.0.840: + Christian: Allow multipass RenderItem rendering for terrain icons too. Should fix sengir's saplings. + +Build 1.6.2-9.10.0.839: + Christian: + Updated FML: + MinecraftForge/FML@913f6f6d36bd179db7c147db0485e99dee693933 Try and use the relaunch log, which should be classloaded.. + MinecraftForge/FML@ac065ff5f76b6c512b346366107efde66e9e1c88 Reset the IWorldGenerator seed for each mod, before calling. That should mean worldgen is consistent and not dependent on mod ordering, or mod sideeffects. + +Build 1.6.2-9.10.0.838: + LexManos: Cache world on all render passes in case some mod disables the first pass. + LexManos: Skipp toss event for null entity items. Closes #732 + +Build 1.6.2-9.10.0.837: + LexManos: Fix bug with custom Fluids. You can now drown in them! + +Build 1.6.2-9.10.0.836: + Christian: + Updated FML: + MinecraftForge/FML@9468e41bbf3ea425c50daa710cf3ada11c82b238 Fix up scala refs, for better results + +Build 1.6.2-9.10.0.835: + Nick: + Adds Temperature to Lava + + Missing lava temperature. Feel free to change it to any value. + 1300K is the typical max for Magma so wasn't sure what was desired. Regardless, better than the same temp as water at 295K :smile: + +Build 1.6.2-9.10.0.834: + Christian: + Updated FML: + MinecraftForge/FML@4a9d0f9bd522e543b76daaf9c49b6214443c595f Add in some log information + Christian: + Updated FML: + MinecraftForge/FML@f157e7a6ecdeac2758fc0eaf547d3e8a763fb15b And more coremod logging + Christian: + Updated FML: + MinecraftForge/FML@ffdd056a18eddb8f28b74435d40e69c956b9dd48 Check keys, not values *sigh* + +Build 1.6.2-9.10.0.833: + Christian: + Updated FML: + MinecraftForge/FML@03989166665956df03aa85472eb13dca2d74a38d And actually instantiate the collection *sigh* + +Build 1.6.2-9.10.0.832: + Christian: + Updated FML: + MinecraftForge/FML@dec9a3924d361bc016cb7f6b3e95764158cf5ae1 Add in "FMLCorePluginContainsMod" in the manifest. If this value is present, FML will attempt to parse your mod jar file as a normal mod file as well, instantiating mod containers there. + +Build 1.6.2-9.10.0.831: + Christian: + Updated FML: + MinecraftForge/FML@24701206808a43b9c7b10d7130c47b5d1e841bb6 Clean up a couple of resources. Also, don't parse jars just because they're in the mods dir + +Build 1.6.2-9.10.0.830: + Christian: + Updated FML: + MinecraftForge/FML@9a5e24e338c6172531efb086a4b584c26d4f1435 Fix stupid derp is stupid. Closes #275 and means sp614x can do his thing + MinecraftForge/FML@ba90b616070ce15793eb05e5afaed62a6f07c6e7 Make sure we only add args to the argument list if a tweaker hasn't already. Should fix LiteLoader compatibility issue. + +Build 1.6.2-9.10.0.829: + LexManos: Fix NPE in enchangint books. + +Build 1.6.2-9.10.0.828: + LexManos: Fix inverted case, search works now. + +Build 1.6.2-9.10.0.826: + ross.swartz: Add stone and cobblestone to Ore Dictionary + ross.swartz: Update OreDictionary.java + LexManos: Addition: Added isAllowedOnBooks hook to Enchantments Closes #589 + +Build 1.6.2-9.10.0.825: + LexManos: Deprecate Block.addCreativeItems, Kill in 1.6.3+ Closes #655 + +Build 1.6.2-9.10.0.824: + mehvids: Add onNeighborTileChange callback to block by generalizing func_96440_m to all blocks rather than just comparators. + +Build 1.6.2-9.10.0.822: + malc.geddes: Added a new function to allow control over whether an entity is dismounted when the entity it is riding goes under water + LexManos: Allow creative tabs to have a search box if they want to Closes #592 + +Build 1.6.2-9.10.0.821: + tommy.stanley96: Fixed Fluid Non-Solid Block Duplication + tommy.stanley96: Fixed Double Item Drop + tommy.stanley96: Fixed Classic Checking + +Build 1.6.2-9.10.0.820: + LexManos: Add optional feature to check entire bounding box for ladders. Closes #709 + +Build 1.6.2-9.10.0.819: + LexManos: Only refresh vanilla tile entities when IDs change. + +Build 1.6.2-9.10.0.818: + LexManos: + Updated FML: + MinecraftForge/FML@f275a24b43559cfdced243ff77e9848c9d458362 Add in some reverse lookup methods for game registry data + MinecraftForge/FML@cb05c8c4aa60a131de92f0a21c06697c8f8896a8 Add missing SideOnly in BaseMod + MinecraftForge/FML@1857064afa9ace796440c19f3275637a6e659375 Merge pull request #266 from grompe/patch-1 + MinecraftForge/FML@182aa9c0cbe61ac69b0d428ead1dc817dd2a2e71 Fixed install.sh not passing arguments to install.py + MinecraftForge/FML@f46a538b41157081c840f647f123513ac4c5a071 Merge pull request #268 from Bo98/sh-args-fix + MinecraftForge/FML@29ef3d5ab412dcabbd67695558880c45011ace82 Update installer. + +Build 1.6.2-9.10.0.817: + tommy.stanley96: + Fluid Render Fix + + Formatting + ohai.iChun: Add Pre and Post events for RenderLivingEvent + ohai.iChun: Add Pre and Post events firing for RendererLivingEntity + ohai.iChun: if statement added + ohai.iChun: Update RendererLivingEntity.java.patch + tommy.stanley96: + Fixed fluids eating each other + + Fluids check for other fluids density before flowing, if their density + is higher they can flow into the other fluid, if not they can't. + CovertJaguar: Add SneakClick bypass to client + cpw: Fix names for water/lava fluids. Closes #689 + cpw: + Add support for loading legacy liquid stacks as new fluid stacks. + Requires having been written with the "liquidname" code from forge 1.5.x + cpw: + Add a translation map for looking up legacy liquid names to convert + to new fluid names. + tommy.stanley96: Fluid Rendering Fixes + cpw: Fix formatting error in PR + cpw: + Tweak setBlock in update tick - it should only send serverside updates for + source blocks. Experimental attempt to fix worldgen issues for fluid blocks + cpw: + Updated FML: + MinecraftForge/FML@57befa89bbbf2bc2fcc4a97b78e07b3f9e23ef9d Fix keybindings being derped + MinecraftForge/FML@1d84e8063e9d0dc73928dba006e6001201285cad Temporarily add a version of 'reobfuscate.py' that will resolve complex reobfuscation graph issues with specialsource. Copy it over 'reobfuscate.py' in the mcp runtime dir. Hopefully will have an MCP/specialsource fix in the coming days. + ohai.iChun: Fix RenderLivingEvent.Pre/Post not being fired by most Renders. + tonkamatt98: + added temperature to fluids + + it could be useful for blocks that are affected by temperature + mitchel.pyl: Fix render colour on bottom of fluids + mitchel.pyl: Fix small derp + +Build 1.6.2-9.10.0.816: + purpleposeidon: + Add an InputStream constructor to WavefrontObject + + It is said that Resource Packs will return InputStreams. And I like putting my models into texture packs which, obviously, give InputStreams rather than URLs. + 7of9: Add cloud height to WorldType + +Build 1.6.2-9.10.0.815-miscchanges: + mitchel.pyl: Fix render colour on bottom of fluids + mitchel.pyl: Fix small derp + +Build 1.6.2-9.10.0.812-miscchanges: + tonkamatt98: + added temperature to fluids + + it could be useful for blocks that are affected by temperature + +Build 1.6.2-9.10.0.811-miscchanges: + ohai.iChun: Fix RenderLivingEvent.Pre/Post not being fired by most Renders. + +Build 1.6.2-9.10.0.810-miscchanges: + cpw: + Updated FML: + MinecraftForge/FML@57befa89bbbf2bc2fcc4a97b78e07b3f9e23ef9d Fix keybindings being derped + MinecraftForge/FML@1d84e8063e9d0dc73928dba006e6001201285cad Temporarily add a version of 'reobfuscate.py' that will resolve complex reobfuscation graph issues with specialsource. Copy it over 'reobfuscate.py' in the mcp runtime dir. Hopefully will have an MCP/specialsource fix in the coming days. + +Build 1.6.2-9.10.0.809-miscchanges: + cpw: Fix formatting error in PR + cpw: + Tweak setBlock in update tick - it should only send serverside updates for + source blocks. Experimental attempt to fix worldgen issues for fluid blocks + +Build 1.6.2-9.10.0.808-miscchanges: + tommy.stanley96: Fluid Rendering Fixes + +Build 1.6.2-9.10.0.807-miscchanges: + cpw: + Add a translation map for looking up legacy liquid names to convert + to new fluid names. + +Build 1.6.2-9.10.0.806-miscchanges: + cpw: + Add support for loading legacy liquid stacks as new fluid stacks. + Requires having been written with the "liquidname" code from forge 1.5.x + +Build 1.6.2-9.10.0.804: + copyboy: Fix getArmorTexture by passing it the subtype + +Build 1.6.2-9.10.0.802: + LexManos: + Re-added deprecated liquids system. To be removed next major MC versions after issues with Fluids are fixed. (reverse-merged from commit 9b5208fa308f22c24e295ce3be38dcafea2857ea) + This WILL be removed and should not be developed against aside for a temporary 1.6 release. + +Build 1.6.2-9.10.0.801: + LexManos: Remove SideOnly(Client) in IBlockAccess.isAirBlock + +Build 1.6.2-9.10.0.800: + LexManos: MinecraftForge/FML@10b16d32da4b7c32b15e69cf1c636505ebbe2540 Use json 2.9.1 nightly for OSX in release json like vanilla does. + +Build 1.6.2-9.10.0.799: + LexManos: General code cleanup of Fluid system. Made Fluid icons and associated functions non-sided. + +Build 1.6.2-9.10.0.798: + LexManos: MinecraftForge/FML@3f21a2c1b413e591f61f2906c3adbadd9c5b09e3 Stupid spaces and windows escaping -.- + +Build 1.6.2-9.10.0.797: + LexManos: MinecraftForge/FML@b2958c9066db8c95bb4260893fbfe00103fc4ba1 Add quotes for paths with spaces -.- + LexManos: Package 'version.json' with universal jar for maunchers to use. It's the json used by the vanilla Minecraft launcher for Forge. + +Build 1.6.2-9.10.0.796: + LexManos: MinecraftForge/FML@9520978b81d4cba5d8b0af0d5f155bd115023795 Use a temporary file for recompile's command line to combat command length to long issues. + +Build 1.6.2-9.10.0.795: + LexManos: Updated FML: MinecraftForge/FML@4981aa3421262c3c1c4705468fe202df8198b9f0 Fix potential NPE in villager skin registry. Closes #678 + +Build 1.6.2-9.10.0.794: + Nick: + Fixes Vanilla Fluid Still Icon Setters + + Fixes null icons from being set for both the still water/lava icons, and sets the correct Icon. + +Build 1.6.2-9.10.0.793: + LexManos: Updated FML: MinecraftForge/FML@c48b48ee15f38d3e794b6eb3499c536226ca5a79 Fix server launching. + +Build 1.6.2-9.10.0.792: + Christian: Fix for new location of mcp logo. + +Build 1.6.2-9.10.0.791: + Christian: + Updated FML: + MinecraftForge/FML@0378355c3720d587652b7792665a8b70bf104eb3 The server.classpath generates the runtime manifest, so it needs the non-debug asm jars. + MinecraftForge/FML@a3f48734ffbbb2eccffdafcd3cbe73824bd1afd6 Fix up jar sanity check code. FML validation of the jar works now and doesn't derp classloading. + MinecraftForge/FML@9947ba85036542a3231e25328d3300f2a5337370 Fix logo handling. no more NPE if the logo can't be found. Also, fix location of mcp logo now. + +Build 1.6.2-9.10.0.790: + Adubbz: + Made eating particles compatible with metadata + + Removed extra spaces + + Made eating particles compatible with metadata + Christian: + Updated FML: + MinecraftForge/FML@e44e8b3112bd56c716a00c19d0be2f15d9128b70 Force a global asset scan prior to mod construction : you should be able to reference assets anywhere in your mod now. + MinecraftForge/FML@20e93a412ee13498babef02d404f57bf5e0fd919 Fix up logos in the mod screen. Clean up some unnecessary casts and suppressions, use the -debug asm library at dev time, since it contains full symbols and code in compiled form. + LexManos: MinecraftForge/FML@b9f4b02cb0b041594656f05de70225df702a8ddd Kill mcp's truncate method, for more useful logs. + LexManos: + Updated FML: + MinecraftForge/FML@7348929819b0ae8ad35419ef5dbf66e66b442858 Kill release time scala libraries, to be re-evaluated after all movement is done and shit is fixed. May require manual instalation for mods that use scala. + MinecraftForge/FML@6de36d78f57f6f08ec586b67b684d0e5406cd436 Coremods now have a primitive dependency capability. Also, we search mods dir for special "TweakClass" manifests. These are using the vanilla tweak mechanism to inject into Minecraft. Helpful for other "platform" systems, when you don't want to have to deal with json changes! + MinecraftForge/FML@d4b30422b64a62a2f8a8c2cccd94cb0fd06154e0 Update build and eclipse workspaces for debug asm. + LexManos: MinecraftForge/FML@c625ef30093abb0755985c74d1f31e2c4cf6cfdd Update Forge signature for new private key + LexManos: Update changelog generator to point to new jenkins. + LexManos: Monkey patch to try and make print flush properly. + +Build 1.6.2-9.10.0.789: + LexManos: Re-add reverted patch AGAIN cpw check your commits -.- + +Build 1.6.2-9.10.0.787: + Christian: + Updated FML: + MinecraftForge/FML@bab4d87ce76baa40200939cc46780b1d3b2ff466 Update FML for new stealth update for 1.6.2 + +Build 1.6.2-9.10.0.786: + Christian: Remove forge ISidedInventory, deprecated since 1.5. + +Build 1.6.2-9.10.0.785: + Christian: + Allow optional rider interaction for entities, thanks for the suggestion Vswe. + + Updated FML: + MinecraftForge/FML@7af5c21d74679d1a53550f9719bba22b2f28dd13 @InstanceFactory was set to look for Fields instead of methods + MinecraftForge/FML@bc9d1fe657c7a0953adc7d4c5ed81c575bdfb0f1 Merge pull request #254 from CaptainShadows/patch-1 + +Build 1.6.2-9.10.0.784: + LexManos: MinecraftForge/FML@c913258ca38e662264bdf4aafbfbef86881c9290 Disable signature check of client for now, it's broken. + Christian: + Updated FML: + MinecraftForge/FML@97269a5e3dc0a0e2e1a79183f9f5f2ee120e90bd Decode the file URL. Hopefully will make things work more.. + MinecraftForge/FML@d4d522c5978ecd7a9195977b3327b441901bb5b4 And don't forget to remove the test code + +Build 1.6.2-9.10.0.781: + LexManos: + Updated FML: + MinecraftForge/FML@dfa3a2665d6782b87713cea26dda558ac990a72a Add MC Version to installed version name. + MinecraftForge/FML@e91431fb707ce3e7e4296ccb8f3b2e5208b4dfac Don't validate signatures on servers, they are not signed. + MinecraftForge/FML@c7ab872c85dd057a4e44e12e34089dfd1a1184b6 Temporarily disable GuiModList's Mod logos. + +Build 1.6.2-9.10.0.780: + LexManos: + Updated FML: + MinecraftForge/FML@c997f2adbc4c11cd8c2abe5f82ccd00b0e954b68 FML now verifies that the minecraft jar is correct and intact. This is intended to discourage those who think that modifying the minecraft jar is still acceptable. + MinecraftForge/FML@0db4624b27a5ecf59ed506ccfc26459ca26ee408 Don't initialize the server. + MinecraftForge/FML@4fa375683fdb7edff67c951fb371ab4a23435308 Fix NPE in new debug line when patch targets don't exist. + +Build 1.6.2-9.10.0.779: + LexManos: Fix accedential reverted patch. + LexManos: Proper return for getRegisteredFluidContainerData thanks Soaryn. Ref issue #634 + +Build 1.6.2-9.10.0.778: + Christian: Make resourcelocation the class available on the server. + +Build 1.6.2-9.10.0.777: + Christian: + Drop two domain related fixes that have been applied in vanilla. + + Updated FML: + MinecraftForge/FML@c47d08c89dfcacb96e36c427593174e08dcb4224 Tweak debug data on patched classes + MinecraftForge/FML@dbf5fe38cee04288e92d57f8782114b452245bce We now generate an adler32 checksum for each patched file and verify at load time that they match. Mismatch won't crash, but will emit a big warning.. + MinecraftForge/FML@e88a0cd13f63904f7317e1a73880611f58820389 Update for stealth update. Thanks mojang! + MinecraftForge/FML@2336002f20e9412a7663781b23c51de0eff6a692 The game is going to exit in face of patch mismatch, unless you force it to run with fml.ignorePatchDiscrepancies in the system properties. + +Build 1.6.2-9.10.0.776: + LexManos: + Updated FML: + MinecraftForge/FML@1d0384f8f664d7002019b865675a5fddf2963b3d Update for 1.6.2 and MCP 8.04 + MinecraftForge/FML@111b0216fdc55f56a8361a584141bca7c9c3f070 Add the jsons for 1.6.2 + MinecraftForge/FML@6f96d89e2bf9313b26eeb4c334a208bf3e1c9ad4 Update eclipse workspaces for 1.6.2 + LexManos: Remove deprecated Liquids API, Use new Fluids system as replacement. + LexManos: Bump version to 9.10 for new MC version and removal of Fluids. + +Build 1.6.1-8.9.0.775: + Christian: + Updated FML: + MinecraftForge/FML@c97ac284a5e7dbdbccbad2f7ccc95252c4aef239 Update ModLoaderFuelHelper.java + MinecraftForge/FML@3a200e901e34ade679e4485307f57bee725bbe94 Fix coremod injection into main system. Should stop double-dipping coremods. + MinecraftForge/FML@2676c8999cbede05b5475ba68bfc25467a67d4fc Update mcp data. fixes #248 + MinecraftForge/FML@5990e29af7b70e343dfd9cf38bb3e033e71a4489 Merge pull request #247 from jk-5/patch-1 + MinecraftForge/FML@adc89722770b7319884619cadc6f10cc9050df24 Add cascadedTweaks. This will allow simple coexistence for any other mod framework using the tweaker system as well. Hi Voxel and LiteLoader! + +Build 1.6.1-8.9.0.774: + xcompwiz: + Makes player-specific spawnpoints dimension aware + + Makes ServerConfigurationManager correctly get player-specific spawn + point for the target dimension + Changes EntityPlayer to store and save a (bed) spawn point for every + dimension, as well as transfer them to respawn "clones" + +Build 1.6.1-8.9.0.773: + LexManos: Re-gather list of Icons when atlas textures are stitched, allows for addition/removal of blocks/items after the atlas's inital constrction. + +Build 1.6.1-8.9.0.772: + LexManos: Fix enchantment effect on single pass items. Closes #644 + +Build 1.6.1-8.9.0.771: + kinglemming: + Forge Fluid System! + + Signed-off-by: King Lemming + LexManos: Mark old liquid system as deperated to be removed next Minecraft release. + +Build 1.6.1-8.9.0.770-newliquid: + LexManos: Mark old liquid system as deperated to be removed next Minecraft release. + +Build 1.6.1-8.9.0.769-newliquid: + richard: Techne model loader (incomplete for debugging) + richard: Complete it, got the bug figured out + Christian: + Updated FML: + MinecraftForge/FML@24c405665105a789a0708a7e30c8bcb96899da6b Add in an optional modid identifier for @SidedProxy. It's main use is when both scala and java @Mods reside in the same package, and you want the @SidedProxy behaviour for a specific @Mod language type. In general it should not be needed otherwise. + MinecraftForge/FML@cd0466395a8f1af3ec44f124bf4088df2d318603 Fix sysout with trailing messages after a newline + LexManos: Disable ImageIO's File based cache, should speed up texturepack loading/stitching. + jadran.kotnik: Added rotation support for all vanilla blocks that can be rotated. + LexManos: + Add NPE protection to ItemStack delegates. Closes #601 + Also cleaned up the names of said delegates, we do not have to follow MCP's crowdsourced names as they are very bad. + Redirected damage through setItemDamage to allow items to have finder control of breaking. + LexManos: Fix Air block check in BlockPortal. + LexManos: Fix hoes being able to till dirt under other blocks, and made it look for air type blocks. + LexManos: Add NPE protection to refernce to MinecraftServer.worldServer + LexManos: Fix AIOOB exception in crash reporting if exception does not have a stack. Possible, but odd. + LexManos: Fixed changelog generation. + LexManos: Forgot most important side, release script. + Christian: Make glass panes and iron fences connect to block sides based on solidity. This does change IBlockAccess, so anything with a custom IBlockAccess may need to implement the new method. + Christian: + Fix server patch FML update + + Updated FML: + MinecraftForge/FML@22738de028a9ba51d43d73857dfb8969985566f0 Attempt to properly fix deadlock if the internal server derps. It should never hang now. Damn you fast computer.. + MinecraftForge/FML@05a854cd2af53ca822ee8b249b0b3bbe44f94675 Small tweaks to the mcp data. Nothing major.. + mitchel.pyl: Fix tripwire patches for solid sides + LexManos: Added EntityLiving to Block.isLadder arguments, deperacating older version. New parameter has the possibility of being null, so modders must take care. Closes #608 + LexManos: Add air checks to BlockSand falling and Piston pushing. Please report any other issues with custom 'Air' blocks in issue #602 + LexManos: Bump Forge revision and mark recomended. + LexManos: Fic Chest content gneeration delegate call. Cloases #609 + LexManos: Add air check to Flint and Steel Ref: #602 + LexManos: Update workspace for new library structure. + LexManos: Update FML to 16launch branch + LexManos: Initial patch update to 1.6, Does not compile, need to update references to the old TexturePack system. + LexManos: Small fixup, need to Update GuiIngameForge for new HUD changes. + LexManos: + Updated FML: + MinecraftForge/FML@6a318ddb784ca8b2bef0f6718089f7beb4d404e0 Fix typo in new packages. + MinecraftForge/FML@3711da9c456d20865a965734cc5aeaf7f5cb5e5d Another typo + MinecraftForge/FML@e35e4b16ff3d6dea547c41f02f2ca31ebe1f74aa More fixups + MinecraftForge/FML@18371bd8c9bd107f774289da35519f593ccc8ee7 Some fixes for updated mcp code + MinecraftForge/FML@ef646d3146e1f285d2cb8e79a74373beffa84774 Merge branch '16launch' + MinecraftForge/FML@7406b38d8ad1bc5c2c641c74f1614b946f246588 1.6.1 + MinecraftForge/FML@12c928c538c1c04d3a21255c747d15468328ace9 Tweak commands patch + MinecraftForge/FML@3f15cd54c2d776ea161aaedbecad9e188d66578f Functional client @ 1.6.1 + MinecraftForge/FML@71a92de5d95fccc4fe17cc39d0836891c6622f4d Client launch for eclipse + LexManos: + Updated FML: + MinecraftForge/FML@8960f6869fbe30d358a40997c47999025c3eae68 Add windows lzma executable http://tukaani.org/xz/ He states that most things are under public domai + n, But I couldn't find an exact reference to this executable. I'm going to assume it under public domain and distribuiting it here is fine. If not someone pleas + e direct me to the apropriate license and I will act accordingly. + MinecraftForge/FML@70cfe24e67adf6872ef1501599e2115e420c2539 Fix wrong project name in distro eclipse launch. + MinecraftForge/FML@7a004087f79b94bc92f29d50eb71288b6c1c968c Add deobf data to src dist. Dont create deobf jar as we ship the lzma Added *.lzma to .gitignore + LexManos: + Updated FML: + MinecraftForge/FML@110cf372eb5aa85df20b248976f1acdefa85e102 Add deobf data to merge-common, workspace is now actually runnable! + LexManos: Support dirty submodules in changelog script. + LexManos: 1.6.1 Update + LexManos: Add new launch configs to dev workspace. + LexManos: Update GuiIngameForge to reflect Mojang changes in 1.6.1 + LexManos: + Updated FML: + MinecraftForge/FML@c418da353f6a8420b095fa737e8b0eae270d31ae Cleanup coremod code, server side working now. + LexManos: Update release script to generate binary patches and include deobf data. + LexManos: Deprecation sweep and update version to 8.9 to reflect 1.6.1 update. + LexManos: MinecraftForge/FML@7fecf2ad6bdd918149a3c43453f6a78bd11e5404 Update mcp URL. + LexManos: Try absolute path, to not confuse jenkins. + LexManos: + Updated FML: + MinecraftForge/FML@1229c4c4ea888f4f69272eed94ef5a53ce79ccda Fix src distrabution, and got rid of pesky common folder in eclipse workspace. src is now installable. + MinecraftForge/FML@902772ed0cb6c22c4cd7ad9b0ec7a02961b5e016 Revert common folder fix, Common folder does nothing, feel free to delete it after first load. + LexManos: Update src distro installer script. Source distro works now! + LexManos: Update ToolMaterial enum helper. + LexManos: Release will now build a installer jar and use the standard 'target' output folder. + LexManos: + Updated FML: + MinecraftForge/FML@29d6c875d0675ffa14428c511bd6ebe9232a486c Add FML Installer logo crated by @ZaverSLO https://twitter.com/ZaverSLO/status/349947190300508162 + MinecraftForge/FML@3d17434510e890574b68c8a181b80c830b5d043a Build installer package for the new client launcher. + MinecraftForge/FML@bf38d947569911dab03319a8b0f1964f36b195b2 Update json samples + MinecraftForge/FML@7037184a4e724300001dfc1f8df2e76a0ec30368 Fix up release JSON + MinecraftForge/FML@dc7d02ebf6c9fc5965344a9aeca79f230a40afb4 Fix json syntax error. + LexManos: + Fix installer unintended replace. + + Updated FML: + MinecraftForge/FML@9b6525e80504ff72a1798cf5797bf148295db776 Point scala downloads to our servers, Launcher doesn't like standard maven repos. + LexManos: + Updated FML: + MinecraftForge/FML@91ecf711092e1610dd10e77cdd517c3324e62d8d Fix -rel json + MinecraftForge/FML@efc369ee83a7b62f605c13e16efad66b63b4bd8c Fix EventHandler annotation. + MinecraftForge/FML@fbd57b32641b540d609314d91fd64350d50b9013 Mods are now loaded as resource packs. Vanilla will scan for valid prefixes based on subdirs of 'ass + ets' which can then be referenced as ResourceLocations with the ":path" notation. + LexManos: + Updated FML: + MinecraftForge/FML@5a97d183dfb13b0f831172a1afef7407347ea7bc Remember to update your patches!!!! + MinecraftForge/FML@f1b533ad87ea08d6e62259c59779bcec1636e2fe Keep these on our servers until the launcher is actually fixed -.- + LexManos: Fix bound texture in Controls screen, Closes #631 and #629 + LexManos: Make Block.setIconName public + LexManos: Update filler block to prevent useless console warning. + LexManos: Fix resource domain when loading icon. Textures should be located in /assets/{domain}/textures/{block|item}/{name}.png Same strcutre as before except 'assets' instead of 'mods'. + LexManos: Update ChestGenHooks for new Dungeon chest strcture. + Christian: + Start: f1b533ad87ea08d6e62259c59779bcec1636e2fe + End: f21cd286ca8e974b75536224a38cc0dacaca8454 + Updated FML: + MinecraftForge/FML@f21cd286ca8e974b75536224a38cc0dacaca8454 Resource packs, part two. FML mods are now resource packs. Vanilla will scan anything under 'assets' and turn it into a resource prefix. Use resourcelocations to look stuff up. + LexManos: Move resource location resolution down to TextureAtlasSprite and allow for sprites that are not stitched. + LexManos: Attempt to gather lastBuild information to fix ChangeLog's off-by-one issue. + LexManos: + Updated FML: + MinecraftForge/FML@6f0eedc9a64e4e246c40335e91b4868ad7f5a9e2 Fixed ClassCastException when loading ModLoader mods + MinecraftForge/FML@8844554da6d5d15756d7b0a9da2f5924006b3190 Merge pull request #243 from jrtc27/modclassloader + MinecraftForge/FML@7aa7221756d62ea1fbc750d7cf7acfdb28d75f2e Fix transformer search + MinecraftForge/FML@5f7df5e742cbc21565cee0d25709b5cb5462127c Revert "Keep these on our servers until the launcher is actually fixed -.-" + MinecraftForge/FML@ad79b9ed86eaf8c2702d79505d78a931c1774560 Fix up some deprecation warnings, and clean up coremod code that's going away. + MinecraftForge/FML@ba3707af22376f8f18103f63db56e4614a9c37db More javadoc cleanup + LexManos: Update dev worksapce: Proper natives location and new server launch profile. + LexManos: TEMPORARY fix for MCP mapping issue. + LexManos: Include MANIFEST file from FML's universal.jar, makes the jar runnable now! + LexManos: + Updated FML: + MinecraftForge/FML@155e8468180c93f1886a64028628764b1b22dd58 Add in support for mods/ as a mod location. Also drop coremods as a location. They go in mods too now. + MinecraftForge/FML@3f4bf61ae6757605b27078c7321de9f640876836 Update key + MinecraftForge/FML@4545beb49d5348d8632e42965627b9837115525b Add deobf-data to setup env. + MinecraftForge/FML@e24f94951741709329208f738000b72933302a24 Fix eclipse workspaces and launch configurations. + MinecraftForge/FML@532bee7ce1c4392ee11f0389d98f0c2be6240aa0 Update to new MCP bugfix version, Fixes: Missing Armor/Item rendering, and Boss health mapping issues. + LexManos: + Updated FML: + MinecraftForge/FML@7ce84491d1d4eada442944e02fc0e50c51f8045c Fix missing argument and startclient/startserver support. + LexManos: Fix domain issue with SoundPool entries. + LexManos: Add hook for EntityWither trying to destroy a block. + LexManos: Fix lether item rendering colors. + LexManos: Render pass sensitive version of Item.hasEffect, Closes #517 + LexManos: Fix compile errors temporarily, dont use till updated to new system. + LexManos: Add missing air checks to WorldGenTrees and ChunkCache Closes #593 + LexManos: Updated FML: MinecraftForge/FML@54e06e841d1c8df24fc30e1ec3a51def67f58858 Move Resource refreshing until affter postInit. + +Build 1.6.1-8.9.0.768: + LexManos: Add missing air checks to WorldGenTrees and ChunkCache Closes #593 + LexManos: Updated FML: MinecraftForge/FML@54e06e841d1c8df24fc30e1ec3a51def67f58858 Move Resource refreshing until affter postInit. + +Build 1.6.1-8.9.0.767: + richard: Techne model loader (incomplete for debugging) + richard: Complete it, got the bug figured out + LexManos: Fix compile errors temporarily, dont use till updated to new system. + +Build 1.6.1-8.9.0.766: + LexManos: Add hook for EntityWither trying to destroy a block. + LexManos: Fix lether item rendering colors. + LexManos: Render pass sensitive version of Item.hasEffect, Closes #517 + +Build 1.6.1-8.9.0.765: + jadran.kotnik: Added rotation support for all vanilla blocks that can be rotated. + +Build 1.6.1-8.9.0.764: + LexManos: + Updated FML: + MinecraftForge/FML@7ce84491d1d4eada442944e02fc0e50c51f8045c Fix missing argument and startclient/startserver support. + LexManos: Fix domain issue with SoundPool entries. + +Build 1.6.1-8.9.0.763: + LexManos: + Updated FML: + MinecraftForge/FML@155e8468180c93f1886a64028628764b1b22dd58 Add in support for mods/ as a mod location. Also drop coremods as a location. They go in mods too now. + MinecraftForge/FML@3f4bf61ae6757605b27078c7321de9f640876836 Update key + MinecraftForge/FML@4545beb49d5348d8632e42965627b9837115525b Add deobf-data to setup env. + MinecraftForge/FML@e24f94951741709329208f738000b72933302a24 Fix eclipse workspaces and launch configurations. + MinecraftForge/FML@532bee7ce1c4392ee11f0389d98f0c2be6240aa0 Update to new MCP bugfix version, Fixes: Missing Armor/Item rendering, and Boss health mapping issues. + +Build 1.6.1-8.9.0.762: + LexManos: Update dev worksapce: Proper natives location and new server launch profile. + LexManos: TEMPORARY fix for MCP mapping issue. + LexManos: Include MANIFEST file from FML's universal.jar, makes the jar runnable now! + +Build 1.6.1-8.9.0.761: + LexManos: + Updated FML: + MinecraftForge/FML@6f0eedc9a64e4e246c40335e91b4868ad7f5a9e2 Fixed ClassCastException when loading ModLoader mods + MinecraftForge/FML@8844554da6d5d15756d7b0a9da2f5924006b3190 Merge pull request #243 from jrtc27/modclassloader + MinecraftForge/FML@7aa7221756d62ea1fbc750d7cf7acfdb28d75f2e Fix transformer search + MinecraftForge/FML@5f7df5e742cbc21565cee0d25709b5cb5462127c Revert "Keep these on our servers until the launcher is actually fixed -.-" + MinecraftForge/FML@ad79b9ed86eaf8c2702d79505d78a931c1774560 Fix up some deprecation warnings, and clean up coremod code that's going away. + MinecraftForge/FML@ba3707af22376f8f18103f63db56e4614a9c37db More javadoc cleanup + +Build 1.6.1-8.9.0.760: + LexManos: Attempt to gather lastBuild information to fix ChangeLog's off-by-one issue. + +Build 1.6.1-8.9.0.759: + LexManos: Move resource location resolution down to TextureAtlasSprite and allow for sprites that are not stitched. + +Build 1.6.1-8.9.0.758: + Christian: + Start: f1b533ad87ea08d6e62259c59779bcec1636e2fe + End: f21cd286ca8e974b75536224a38cc0dacaca8454 + Updated FML: + MinecraftForge/FML@f21cd286ca8e974b75536224a38cc0dacaca8454 Resource packs, part two. FML mods are now resource packs. Vanilla will scan anything under 'assets' and turn it into a resource prefix. Use resourcelocations to look stuff up. + +Build 1.6.1-8.9.0.757: + LexManos: Update ChestGenHooks for new Dungeon chest strcture. + +Build 1.6.1-8.9.0.756: + LexManos: Make Block.setIconName public + LexManos: Update filler block to prevent useless console warning. + LexManos: Fix resource domain when loading icon. Textures should be located in /assets/{domain}/textures/{block|item}/{name}.png Same strcutre as before except 'assets' instead of 'mods'. + +Build 1.6.1-8.9.0.755: + LexManos: Update ToolMaterial enum helper. + LexManos: Release will now build a installer jar and use the standard 'target' output folder. + LexManos: + Updated FML: + MinecraftForge/FML@29d6c875d0675ffa14428c511bd6ebe9232a486c Add FML Installer logo crated by @ZaverSLO https://twitter.com/ZaverSLO/status/349947190300508162 + MinecraftForge/FML@3d17434510e890574b68c8a181b80c830b5d043a Build installer package for the new client launcher. + MinecraftForge/FML@bf38d947569911dab03319a8b0f1964f36b195b2 Update json samples + MinecraftForge/FML@7037184a4e724300001dfc1f8df2e76a0ec30368 Fix up release JSON + MinecraftForge/FML@dc7d02ebf6c9fc5965344a9aeca79f230a40afb4 Fix json syntax error. + LexManos: + Fix installer unintended replace. + + Updated FML: + MinecraftForge/FML@9b6525e80504ff72a1798cf5797bf148295db776 Point scala downloads to our servers, Launcher doesn't like standard maven repos. + LexManos: + Updated FML: + MinecraftForge/FML@91ecf711092e1610dd10e77cdd517c3324e62d8d Fix -rel json + MinecraftForge/FML@efc369ee83a7b62f605c13e16efad66b63b4bd8c Fix EventHandler annotation. + MinecraftForge/FML@fbd57b32641b540d609314d91fd64350d50b9013 Mods are now loaded as resource packs. Vanilla will scan for valid prefixes based on subdirs of 'ass + ets' which can then be referenced as ResourceLocations with the ":path" notation. + LexManos: + Updated FML: + MinecraftForge/FML@5a97d183dfb13b0f831172a1afef7407347ea7bc Remember to update your patches!!!! + MinecraftForge/FML@f1b533ad87ea08d6e62259c59779bcec1636e2fe Keep these on our servers until the launcher is actually fixed -.- + LexManos: Fix bound texture in Controls screen, Closes #631 and #629 + +Build 1.6.1-8.9.0.751: + LexManos: + Updated FML: + MinecraftForge/FML@1229c4c4ea888f4f69272eed94ef5a53ce79ccda Fix src distrabution, and got rid of pesky common folder in eclipse workspace. src is now installable. + MinecraftForge/FML@902772ed0cb6c22c4cd7ad9b0ec7a02961b5e016 Revert common folder fix, Common folder does nothing, feel free to delete it after first load. + LexManos: Update src distro installer script. Source distro works now! + +Build 1.6.1-8.9.0.750-1.6: + LexManos: + Updated FML: + MinecraftForge/FML@1229c4c4ea888f4f69272eed94ef5a53ce79ccda Fix src distrabution, and got rid of pesky common folder in eclipse workspace. src is now installable. + MinecraftForge/FML@902772ed0cb6c22c4cd7ad9b0ec7a02961b5e016 Revert common folder fix, Common folder does nothing, feel free to delete it after first load. + LexManos: Update src distro installer script. Source distro works now! + +Build 1.6.1-8.9.0.749: + LexManos: Update workspace for new library structure. + LexManos: Update FML to 16launch branch + LexManos: Initial patch update to 1.6, Does not compile, need to update references to the old TexturePack system. + LexManos: Small fixup, need to Update GuiIngameForge for new HUD changes. + LexManos: + Updated FML: + MinecraftForge/FML@6a318ddb784ca8b2bef0f6718089f7beb4d404e0 Fix typo in new packages. + MinecraftForge/FML@3711da9c456d20865a965734cc5aeaf7f5cb5e5d Another typo + MinecraftForge/FML@e35e4b16ff3d6dea547c41f02f2ca31ebe1f74aa More fixups + MinecraftForge/FML@18371bd8c9bd107f774289da35519f593ccc8ee7 Some fixes for updated mcp code + MinecraftForge/FML@ef646d3146e1f285d2cb8e79a74373beffa84774 Merge branch '16launch' + MinecraftForge/FML@7406b38d8ad1bc5c2c641c74f1614b946f246588 1.6.1 + MinecraftForge/FML@12c928c538c1c04d3a21255c747d15468328ace9 Tweak commands patch + MinecraftForge/FML@3f15cd54c2d776ea161aaedbecad9e188d66578f Functional client @ 1.6.1 + MinecraftForge/FML@71a92de5d95fccc4fe17cc39d0836891c6622f4d Client launch for eclipse + LexManos: + Updated FML: + MinecraftForge/FML@8960f6869fbe30d358a40997c47999025c3eae68 Add windows lzma executable http://tukaani.org/xz/ He states that most things are under public domai + n, But I couldn't find an exact reference to this executable. I'm going to assume it under public domain and distribuiting it here is fine. If not someone pleas + e direct me to the apropriate license and I will act accordingly. + MinecraftForge/FML@70cfe24e67adf6872ef1501599e2115e420c2539 Fix wrong project name in distro eclipse launch. + MinecraftForge/FML@7a004087f79b94bc92f29d50eb71288b6c1c968c Add deobf data to src dist. Dont create deobf jar as we ship the lzma Added *.lzma to .gitignore + LexManos: + Updated FML: + MinecraftForge/FML@110cf372eb5aa85df20b248976f1acdefa85e102 Add deobf data to merge-common, workspace is now actually runnable! + LexManos: Support dirty submodules in changelog script. + LexManos: 1.6.1 Update + LexManos: Add new launch configs to dev workspace. + LexManos: Update GuiIngameForge to reflect Mojang changes in 1.6.1 + LexManos: + Updated FML: + MinecraftForge/FML@c418da353f6a8420b095fa737e8b0eae270d31ae Cleanup coremod code, server side working now. + LexManos: Update release script to generate binary patches and include deobf data. + LexManos: Deprecation sweep and update version to 8.9 to reflect 1.6.1 update. + LexManos: MinecraftForge/FML@7fecf2ad6bdd918149a3c43453f6a78bd11e5404 Update mcp URL. + LexManos: Try absolute path, to not confuse jenkins. + +Build 1.6.1-8.9.0.748-1.6: + LexManos: Try absolute path, to not confuse jenkins. + +Build 1.5.2-7.8.1.738: + LexManos: Fic Chest content gneeration delegate call. Cloases #609 + LexManos: Add air check to Flint and Steel Ref: #602 + +Build 1.5.2-7.8.1.737: + LexManos: Bump Forge revision and mark recomended. + +Build 1.5.2-7.8.0.736: + LexManos: Added EntityLiving to Block.isLadder arguments, deperacating older version. New parameter has the possibility of being null, so modders must take care. Closes #608 + LexManos: Add air checks to BlockSand falling and Piston pushing. Please report any other issues with custom 'Air' blocks in issue #602 + +Build 1.5.2-7.8.0.735: + mitchel.pyl: Fix tripwire patches for solid sides + +Build 1.5.2-7.8.0.734: + Christian: + Fix server patch FML update + + Updated FML: + MinecraftForge/FML@22738de028a9ba51d43d73857dfb8969985566f0 Attempt to properly fix deadlock if the internal server derps. It should never hang now. Damn you fast computer.. + MinecraftForge/FML@05a854cd2af53ca822ee8b249b0b3bbe44f94675 Small tweaks to the mcp data. Nothing major.. + +Build 1.5.2-7.8.0.733: + Christian: Make glass panes and iron fences connect to block sides based on solidity. This does change IBlockAccess, so anything with a custom IBlockAccess may need to implement the new method. + +Build 1.5.2-7.8.0.732: + LexManos: Forgot most important side, release script. + +Build 1.5.2-7.8.0.731: + LexManos: Fixed changelog generation. + +Build 1.5.2-7.8.0.730: + LexManos: Fix AIOOB exception in crash reporting if exception does not have a stack. Possible, but odd. + +Build 1.5.2-7.8.0.729: + LexManos: Fix hoes being able to till dirt under other blocks, and made it look for air type blocks. + LexManos: Add NPE protection to refernce to MinecraftServer.worldServer + +Build 1.5.2-7.8.0.728: + LexManos: Fix Air block check in BlockPortal. + +Build 1.5.2-7.8.0.727: + LexManos: + Add NPE protection to ItemStack delegates. Closes #601 + Also cleaned up the names of said delegates, we do not have to follow MCP's crowdsourced names as they are very bad. + Redirected damage through setItemDamage to allow items to have finder control of breaking. + +Build 1.5.2-7.8.0.726: + LexManos: Disable ImageIO's File based cache, should speed up texturepack loading/stitching. + +Build 1.5.2-7.8.0.725: + Christian: + Updated FML: + MinecraftForge/FML@24c405665105a789a0708a7e30c8bcb96899da6b Add in an optional modid identifier for @SidedProxy. It's main use is when both scala and java @Mods reside in the same package, and you want the @SidedProxy behaviour for a specific @Mod language type. In general it should not be needed otherwise. + MinecraftForge/FML@cd0466395a8f1af3ec44f124bf4088df2d318603 Fix sysout with trailing messages after a newline + +Build 1.5.2-7.8.0.724-newliquid: + jeffreykog: RenderGameOverlayEvent.Post(ALL) is now called in GuiIngameForge + LexManos: Fix arbitrary GUIContainer text coloring by forcing lighting to be disabled. Closes #594 + Christian: Patch village distance checking to use floats instead of ints, to avoid int wrapping + Christian: Fix isAir check to see if a portal can light. Should fix w/Railcraft and others + LexManos: Make changelog generation non-fatal untill we get jenkins issue figured out. + LexManos: Add cache for ASM Event Handler bridge classes, should not need to redefine a class to invoke the same method on different instances. + LexManos: Ignore registration of a object that is already registered. Preventing duplicate callback invocations. Thanks King_Lemming for pointing this out. + Christian: + Updated FML: + MinecraftForge/FML@6f3da9736531153629fb4213e3b2cae776bfb50a Don't throw an exception if the scala adapter find a java proxy. I may add some distinguishers to @SidedProxy so you know which mod it's for. + MinecraftForge/FML@59fe905695421a5be9370b0009ef794abaaf75bb Don't continue trying to send events to mods that depend on errored mods. + +Build 1.5.2-7.8.0.723: + Christian: + Updated FML: + MinecraftForge/FML@6f3da9736531153629fb4213e3b2cae776bfb50a Don't throw an exception if the scala adapter find a java proxy. I may add some distinguishers to @SidedProxy so you know which mod it's for. + MinecraftForge/FML@59fe905695421a5be9370b0009ef794abaaf75bb Don't continue trying to send events to mods that depend on errored mods. + +Build 1.5.2-7.8.0.722: + jeffreykog: RenderGameOverlayEvent.Post(ALL) is now called in GuiIngameForge + +Build 1.5.2-7.8.0.721: + LexManos: Ignore registration of a object that is already registered. Preventing duplicate callback invocations. Thanks King_Lemming for pointing this out. + +Build 1.5.2-7.8.0.720: + LexManos: Add cache for ASM Event Handler bridge classes, should not need to redefine a class to invoke the same method on different instances. + +Build 1.5.2-7.8.0.719: + Christian: Fix isAir check to see if a portal can light. Should fix w/Railcraft and others + LexManos: Make changelog generation non-fatal untill we get jenkins issue figured out. + +Build 1.5.2-7.8.0.716: + Christian: Patch village distance checking to use floats instead of ints, to avoid int wrapping + +Build 1.5.2-7.8.0.715: + LexManos: Fix arbitrary GUIContainer text coloring by forcing lighting to be disabled. Closes #594 + +Build 1.5.2-7.8.0.713: + Christian: + Updated FML: + MinecraftForge/FML@843a13c1ab1e3901160082fa63c557243fb64675 Try and clean up cycle detection output a bit + MinecraftForge/FML@82e9de8641c6a559eec711ea6d1e940d99cbc98f More tweaks to the mod sorting code + MinecraftForge/FML@ac97370f94d10beee5f021795cddda827c4411d4 Add in a pretty sorting error screen + MinecraftForge/FML@f1d68ed4c82cd28e50ec6a0befc55ff0165bfe08 Throw the sorting exception + MinecraftForge/FML@edc1fb24e2cad9badd2dd18ccccd590d77156e18 Send the "suspect list", and print the suspect versions + +Build 1.5.2-7.8.0.711: + bloodshot: + Add support for servers to register dimensions on client. + + In order to support multi-worlds such as MultiVerse, the server needs the + ability to register dimensions on client or many forge mods such as IC2 + will not function correctly. This has been an issue for MCPC which + provides both Forge and Bukkit support to players. By adding the + DimensionRegisterPacket class, MCPC now has the ability to send the + required packet to client to register a dimension with DimensionManager. + LexManos: Cleanup Dimension Registration packet, generate takes no arguments, and added fix to world to prevent providers from overwriting the dimension ID. + LexManos: Fix potential GL issue when atlas animations bind textures without informating RenderEngine. + +Build 1.5.2-7.8.0.710: + LexManos: + Introduced the framework for Forge's packet handler system. Heavily based off FMLPacket. + Packet splitting and reconstruction is handled. + +Build 1.5.2-7.8.0.708: + Christian: + Updated FML: + MinecraftForge/FML@3bf50c4bbe82f0cc317dafcf2a569cb5210bc738 Fix server side derp with Forge network packet handler + +Build 1.5.2-7.8.0.707: + LexManos: Fix type in biome list. + LexManos: Fixed creative tab rendering with invalid color closes #588 + +Build 1.5.2-7.8.0.706: + Christian: + Add forge network handling support + + Updated FML: + MinecraftForge/FML@4922e90d81d8b8b9374c4d04858a06c5bf03965c Separate network mod config from setup. Allows for Forge Packet Handler nicety + +Build 1.5.2-7.8.0.705: + mehvids: Add hitInfo field to MovingObjectPosition for when an int doesn't suffice + +Build 1.5.2-7.8.0.704: + LexManos: Fix another NPE when Items are in Block range with custom renderers, Modders keep your items out of the block range! Closes #581 + LexManos: Fixed render count for stacks of blocks 40+ and distrabution of items based on scale. Closes #579 + +Build 1.5.2-7.8.0.703: + Christian: Remember x & z passed to getTopSolidOrLiquidBlock and use it for foliage tests. Fixes #575 + +Build 1.5.2-7.8.0.702: + Christian: Fix blending on the hotbar. Closes #574 + +Build 1.5.2-7.8.0.701: + Christian: + Updated FML: + MinecraftForge/FML@23b070c7d02a8da44bf04c2f9ba2b485a44ad967 Alternative @SidedProxy setter for Scala. + MinecraftForge/FML@2cca7aa759b4b6c3a128ce43bbd924e4762c43c2 Some more Scala @SidedProxy adjustments. Now properly supporting pure singletons, i.e. object C { @SidedProxy(...) var proxy: P = null } Removed fallback, as all such singletons are properly handled by the new code now, and class implementations fall back to the code also used for plain Java mods. + MinecraftForge/FML@8517a824e5251c409e05999d42fc6d70497609f5 Merge declaration and initialization of a variable. + MinecraftForge/FML@a3a920437f3ede6841ae2c449a38975b02b28088 Merge pull request #230 from fnuecke/master + Christian: Add in a very simple stencil bit registry to try and arbitrate between mods wanting to use stencil bits in rendering + +Build 1.5.2-7.8.0.700: + hbiede: + Added oreQuartz + + Gives a default quartz Ore Dictionary (I know of at least 3 used by different mods that use Nether Quartz as an equivalent). + gholdampf: updated FurnaceRecipes.java.patch + mike.stengel: Created an ItemStack sensitive version of canHarvestBlock. + +Build 1.5.2-7.8.0.699: + CovertJaguar: + Possible NPE if the liquid isn't in the LD + + https://github.com/BuildCraft/BuildCraft/issues/787 + +Build 1.5.2-7.8.0.698: + LexManos: Fix NPE when rendering EntityItems that are not blocks but int he block range. + +Build 1.5.2-7.8.0.697: + LexManos: Prevent water from dropping snowballs. + +Build 1.5.2-7.8.0.696: + LexManos: Fire EntityJoinWorled event for forced entities {Players and there mounts} as well, just ignore the cancelled value. + +Build 1.5.2-7.8.0.695: + LexManos: + MinecraftForge/FML@787c0c4a6af3af60928b3a90f383a305a17a4347 Don't spit error on LWJGL not supporting 4.3 just warning. + Make custom item renderers attempt to use EQUIPPED type when running first person. For backwards compatiblity, will be removed in 1.6. + And a small change to make the 1.6 check not print it's stack trace. + +Build 1.5.2-7.8.0.693: + purpleposeidon: Fix render bounding box of trapped double chests + jholcroft: + Make getStencilBits static + + Made getStencilBits static so it can be called. + +Build 1.5.2-7.8.0.692: + LexManos: Move Partical rendering down in the order, after everything else. Should fix particals rendering behind water, digging process, and selection. + +Build 1.5.2-7.8.0.691: + LexManos: Fixed NPE in rendering dragged items. + +Build 1.5.2-7.8.0.690: + LexManos: + Attempt to allocate a 8-bit stencil buffer when creating Minecraft's display context. If that throws an error it will revert back to it's default values. + Also contains a method for modders to rereive how many bits the context was created with. Closes #552 + +Build 1.5.2-7.8.0.689: + LexManos: EntityPlayer sensitive version of Item.isValidArmor, deprecated older version. Closes #551 + LexManos: Changed to just plane entity for flexability. + LexManos: New RenderPlayer and RenderLiving events. Closes #493 + +Build 1.5.2-7.8.0.688: + pahimar: Update forge_at.cfg + +Build 1.5.2-7.8.0.687: + mitchpetrie29: Separate EQUIPPED and EQUIPPED_FIRST_PERSON Item Render Types + +Build 1.5.2-7.8.0.686: + LexManos: Capture and fire the PlaySoundAtEntity event for The client entity. Minecraft does some odd bypasses for no good reason. Thanks iPixile for reporting this. + +Build 1.5.2-7.8.0.685: + LexManos: + Updated FML: + MinecraftForge/FML@99bb50d8f8d27217ba58a41c802a504213e99461 Improved Entity Spawn Error + MinecraftForge/FML@c0cca7f41d5b080e39dd8d3d6cfc329295c822e6 Merge pull request #227 from CovertJaguar/patch-1 + MinecraftForge/FML@49111e9c5cffab49ec35f965801c3f0496f6def6 Add version detection to astyle and print error if it doesnt detect version 2.0+ + Also fixed astyle's config for max-instatement-indent that sometimes caused crashes with certian versions of astyle. + +Build 1.5.2-7.8.0.684: + LexManos: + Update to 1.5.2 PR. + MinecraftForge/FML@f0bba74a4748935ef3a715ae2f45feb75cc20376 Update for minecraft 1.5.2 + MinecraftForge/FML@62fdbad74c2507d147ecab56f56029135d88c6f5 Update MCP's md5 for the fixed srg files. + LexManos: + Updated FML: + MinecraftForge/FML@00f00b17bf0da262e6fe3e327ca2deedf7146305 Fix scalac detection to actually use the located command instead of defaulting to 'scalac' + LexManos: Fixed hardcoding of MC version in debug text. + LexManos: Bump version number for 1.5.2 + +Build 1.5.2-7.8.0.683-1.5.2: + LexManos: Bump version number for 1.5.2 + +Build 1.5.1-7.7.2.682: + cadyyan: Fixed build error handling using invalid Python syntax. + +Build 1.5.2-7.7.1.681-1.5.2: + LexManos: Fixed hardcoding of MC version in debug text. + +Build 1.5.2-7.7.1.680-1.5.2: + LexManos: + Updated FML: + MinecraftForge/FML@00f00b17bf0da262e6fe3e327ca2deedf7146305 Fix scalac detection to actually use the located command instead of defaulting to 'scalac' + +Build 1.5.1-7.7.2.679: + LexManos: Changelog generator will now bundle failed builds with the next successful build. Should make it look better and prevent the version numbers from being confusing. + +Build 1.5.1-7.7.2.678: + LexManos: Bump version to 7.7.2 so I can make a release. + +Build 1.5.1-7.7.1.676: + tobias: Fixed ListenerListInst not being rebuild after unregistering a listener, causing calls to unregistered event listeners. + tobias: Replaced derpy tabs with spaces + +Build 1.5.1-7.7.1.675: + kinglemming: + -Add vanilla Ores to the Ore Dictionary. No recipe replacement required. + -Add NBT-sensitive getMaxDamage() for ItemStack. + + Signed-off-by: King Lemming + +Build 1.5.1-7.7.1.674: + LexManos: Attempted a fix for the changelog generation, should print all builds back to 1 now. + +Build 1.5.1-7.7.1.673: + Christian: + Updated FML: + MinecraftForge/FML@cf9b5b445ba284d389c7e32a03d9c8ef43469042 Don't leave scala running in interactive mode. + +Build 1.5.1-7.7.1.672: + Christian: + Updated FML: + MinecraftForge/FML@cfda7fc738ce6079f625a3822ebff5e7e0db5669 Fix URL for MCP + MinecraftForge/FML@781c68121626321e0efddaf4c4db9f1b8b5911db Some fixups for scala compilation in MCP. Scala should now be compilable and reobfuscatable with srgnames in MCP. + Christian: Try and be less noisy about world leaks + Christian: ItemStack delegation to Item for damage values. + LexManos: Fix rotation issue with non-block items. + Christian: + Updated FML: + MinecraftForge/FML@ddadf93ca1d648d88fdb61c9625cd675c3650ccd Fix when an old scalac is present on the system to not fail the build + Christian: + Updated FML: + MinecraftForge/FML@aa200923f0fe0c548faa4f103d803ade2e49d19d Fix patch derp. + +Build 1.5.1-7.7.1.667: + Christian: + Updated FML: + MinecraftForge/FML@dcf069ca52738a7bb7bde01f1c7ebd2e06cd0ac6 Attempt to reduce lines on the screen for id mismatch. Hopefully prevents game crashes. + MinecraftForge/FML@58ba24add2a96bf4c079d5919f2d90dcc2f380e4 Fix possible NPEs in GameData + +Build 1.5.1-7.7.1.666: + Christian: + Updated FML: + MinecraftForge/FML@a3b5eaacfdd9218ef68d3dc064bba729b797cb3d Fix a small modloader compatibility derp: closes #222 + MinecraftForge/FML@677a6e578e84109702365da4a784f9a57d8c9957 Fix scala supporting SidedProxy. It should work now. + MinecraftForge/FML@334a76de75f2b417f04c23526c7e66ceb48e0de2 Update FMLDeobfuscatingRemapper.java + MinecraftForge/FML@42f1d8795599e0d1a516a1fdd7488a09b77e4565 Merge pull request #224 from Glought/master + MinecraftForge/FML@2dcabe01232b48009acbca6724565598761f561b Add a to string to fmlmodcontainer: should mean less derpy cyclic dependency data + MinecraftForge/FML@11ac46daebe901a6012a09ba5f6fe44af5b1be06 Fixing the GameRegistry. Now it is possible to register a Block with a BlockItem using following code: MyBlock myBlock = new MyBlock(); GameRegistry.registerBlock(myBlock, MyBlockItem.class, "myBlock"); where MyBlockItem class has one constructor with signature: public MyBlockItem(int id, Block block) + MinecraftForge/FML@c3fda11d100f9db7c32ef212ac37eade3e35d701 Merge pull request #225 from MarcinSc/master + Christian: Add forge/FML data to the f3 screen. + +Build 1.5.1-7.7.1.665: + LexManos: + Forge now takes control of GuiIngame's renderGameOverlay, and spits out a ton of events to give modders control of how the screen is rendered. + Inital draft for community feedback. + +Build 1.5.1-7.7.1.664: + LexManos: Allow spawning of Bonemeal particles even if block is not set. + +Build 1.5.1-7.7.1.663: + LexManos: Fix the BLOCK_3D render helper for items that aren't an instance of ItemBlock Closes #533 + LexManos: Fix replaceable checks to work on blocks with a non-replacable material, Closes #532 + LexManos: Restore world unload event for client worlds Closes #531 + +Build 1.5.1-7.7.1.662: + LexManos: Fix logic issue in CrashReportCategory patch, blame Jadedcat for sloppy quick patch. + +Build 1.5.1-7.7.1.661: + lhb: + Fix for ClassCastException when attempting to fetch Biome types from BiomeDictionary + + Attempting to fetch the BiomeDictionary types linked to a biome throws a ClassCastException. This fixes that + lhb: getBiomesForType will cause the same problem + +Build 1.5.1-7.7.1.660: + Christian: + Updated FML: + MinecraftForge/FML@b3d4ea05ec633fb1898e97febf786f1a3e420986 Fix possible NPE in findItemStack, closes #218 + Christian: + Simple block recolouring API: closes #525 + Fix up documentation on "rotation" API: it is up to the mod to decide interpretation of "rotation" for the mod/block. + +Build 1.5.1-7.7.1.659: + LexManos: Fix return value of EnchantPower hook, closes #518 + LexManos: Adds a field to the player for maximum health. Player.dat additions are present, getMaxHealth() needs a magic number for spawning. Closes #527 + +Build 1.5.1-7.7.1.657: + LexManos: New hook to allow Items to render Helmet overlays like pumpkins do. + +Build 1.5.1-7.7.1.656: + Christian: + Updated FML: + MinecraftForge/FML@394f424185a044afcd6b31f400e731478171dd18 Fix to output versions in crash logs + MinecraftForge/FML@8f35adca7a41c280a4b63d4787f042f615966cac Use more expressive language when a version specifier is a simple unbounded above condition + +Build 1.5.1-7.7.1.655: + Christian: + Updated FML: + MinecraftForge/FML@5673c1dd2966536000c2b3f17f85131204c4a291 Add srgname to registerTileEntityWithAlternatives + MinecraftForge/FML@7aea09f4ca2f087d59ff6cb0de1c8e3e8b9ea4df Add type info to properly deobfuscate overloaded fields. Fixes #210. + MinecraftForge/FML@edffd04ed2e89ece75189f76b92ae47643ec92f8 Add in some caching of the read field descriptions for efficiency + MinecraftForge/FML@0daf1a6df4203d97be65a76a46550f6ad22ccc79 Merge branch 'agaricusb-fix-deobf-field' + MinecraftForge/FML@ada52078c75fdfa506a4287c112d01d9af961d5a Merge pull request #216 from agaricusb/add-srgname + +Build 1.5.1-7.7.1.654: + jmacwilliams: fixed a bug that prevented proper lava generation near bedrock + +Build 1.5.1-7.7.1.653: + LexManos: Fix AIOOB in crash report stack trimming. + +Build 1.5.1-7.7.1.652: + pahimar: Fix a derp in that we provision the various arrays for a face, even if we are not going to parse data into it. Solves NPEs for when obj models that don't have texture coordinates attempt to render. + pahimar: Missed a bit + +Build 1.5.1-7.7.1.651: + LexManos: Cave and Ravine gen will now take into account the Biomes top and foller block, allowing them to break the surface in modded biomes. Beaches, MushroomIslands and Deserts are exempt from this check to preserve vanilla world gen functionality. Closes #491 + +Build 1.5.1-7.7.1.650: + Christian: Fix offset in AdvancedModelLoader. *doh* + +Build 1.5.1-7.7.1.649: + LexManos: Added NBT data to liquid stacks. Closes #501 + LexManos: Added a small method in the Block.java to specify the amount of enchanting power it can supply to an enchanting table. Closes #508 + +Build 1.5.1-7.7.1.648: + LexManos: Item callback for EntityItem update tick. Closes #426 + LexManos: Add Item 'swing' callback for use when playing the arm swing animation. Closes #505 + +Build 1.5.1-7.7.1.647: + Christian: Delete sneaky extra file + +Build 1.5.1-7.7.1.645: + LexManos: Re-add and mark deprecated the old signature for ForgeHooksClient.getArmorTexture. + +Build 1.5.1-7.7.1.644: + Christian: + Deprecate preloadTexture, make it a no-op. Should stop derpiness with new + texturing system performance tweaks. + +Build 1.5.1-7.7.1.643: + LexManos: + Updated FML: MinecraftForge/FML@4836b3272a9b292c62816c1d1f9e845486753839 Re-worked the Texture patches, optifine helper function, and re-added support fo + r dynamically rotating the texture for mod authors who do things horribly wrong. + +Build 1.5.1-7.7.1.642: + Christian: + Fix performance of texture uploads + + Updated FML: + MinecraftForge/FML@00c788308881a07a683e17e2e9382313f3719b45 Very significant improvement in performance by using glSubImage to upload data. Inspired by frequent complaints about performance of hires texture packs. They probably still need a beefy system but should work. Hopefully I can figure out why the subImage GL side copy isn't working properly for an even more significant speed boost. But this gets things started. + MinecraftForge/FML@57ad221cc6d9605b9d521f86620c2a31f922ac24 And add the patches *sigh* + Christian: + Updated FML: + MinecraftForge/FML@a31607ae7d0214101679a1ecf1ae8032a5257eda Fix compilation derp, and clean up rotation helper. + +Build 1.5.1-7.7.1.640: + LexManos: Small optimization for usages of Minecraft.getGLMaximumTextureSize(), only need to calculate it once. + LexManos: Small bugfix in Stitcher that was preventing ti from fully filling the possible texture space. Should lower the amount of empty space in textures. + LexManos: + Optimize Texture loops a bit for non-rotated textures. Should help the FPS loss on higher resolution texture packs. If it becomes a major issue we may have to look into a more optimized animation system. + + https://mojang.atlassian.net/browse/MC-13206 + +Build 1.5.1-7.7.1.639: + LexManos: Fix scoreboard saving bug caused by our fix of vanilla map saves. + +Build 1.5.1-7.7.1.638: + LexManos: Updated FML: MinecraftForge/FML@1de89525cc2265bdce8704d9bd0d31c57bca4d97 Fixed issue with instalation when java/javac commands had quotes. + LexManos: Deprecate long dead interface that moved to FML. remvoe next MC version. + +Build 1.5.1-7.7.1.637: + LexManos: Updated FML: MinecraftForge/FML@704a70902fca3de620375116a33dccd3d6d576d0 Sanitize input to isRemappedClass to use '/' as a package seperator like the srg files. + +Build 1.5.1-7.7.1.636: + froggytheturtle: + This allows the result of the explosion to take into account metadata, + tile entities, or even to cancel it altogether. + + Allowed block exploding to take into account tile entity and metadata + LexManos: New hook to allow Items to provide there own armor models. Closes #487 + +Build 1.5.1-7.7.1.635: + LexManos: Fix item deletion in creative menu for items that are the same id/meta but differnet NBT's. Closes #479 + LexManos: Untag NBTTagList.removeTag as client side only, allowing simple removal ont he server side. Closes #477 + +Build 1.5.1-7.7.1.634: + LexManos: Allow items to provide there own FontRenderer for there tooltips. Added for #463 + +Build 1.5.1-7.7.1.633: + ProjectZulu: Added maxCanSpawnInChunk event to allow overriding of creature chunk spawn cap + ProjectZulu: Clarify Factory call and Event Functionality + LexManos: Add function to remove categories from a configuration, indavidual properties can be removed using ConfigCategory.remove() Closes #462 + +Build 1.5.1-7.7.1.632: + LexManos: + Pulled Biome Tag System by Emasher, Closes #433 + An issue with biome adding mods which is becoming increasingly annoying for players, is that many mod authors that add biome specific world generation or mobs in their mods, for the most part, hard code them to work with vanilla biomes only. This becomes a huge problem when it's difficult to even find a vanilla biome, let alone a specific one, when biome mods are installed. + + A simple solution to this problem is a tag system for biomes that allows mod authors to set up their world generators, or mobs to generate or spawn in biomes that have been registered with a specific tag such as "FOREST", or "FROZEN". I wrote such a system a few months ago, which I've been using with my own mods, and have made available to anyone who wants to use it. Since then, I've had requests from mod authors and players alike to try and get it, or at least similar functionality, into Forge, where other mod authors will be more comfortable using it. + + Aside from the tags, it also includes a rule based system to classify biomes that have not already been registered with it when information is requested on them (You can opt out of this by registering a biome as type "NULL"). And additionally, the ability to register IWorldGenerators for specific biomes, or biome types (tags) to speed up chunk generation a little bit. + +Build 1.5.1-7.7.1.631: + LexManos: Deprecate IArmorTextureProvider, moved to Item. And exposed more information to the function. Closes #365 + +Build 1.5.1-7.7.1.630: + LexManos: Updated FML: MinecraftForge/FML@570faeb790745c35403c67fabab57651b71da576 Added the ability to save transformed classes to disc for debugging. + LexManos: Add checking for 'ENUM$VALUES' in EnumHelper. Eclipse uses it's own internal compiler which does not follow the java standard of making the values field names $VALUES and private. Instead its public and ENUM$VALUES. Closes #502 + +Build 1.5.1-7.7.1.629: + LexManos: Revert MinecraftForge/MinecraftForge@f594109b30c87f5a0996eee9e8c4513380733cee If concurancy issues arise we will reassess. The provided solution caused entities to be removed incorrectly and cause 'invisible' entities client side. + +Build 1.5.1-7.7.1.628: + LexManos: Fix EntityPlayer passed to Bonemeal event. + +Build 1.5.1-7.7.1.627: + LexManos: Fix off-by-one in rotated textures. + LexManos: Removed index bounds checking in some chunk functions, if you error blame Grum. + +Build 1.5.1-7.7.1.625: + LexManos: Fix vanilla texture bug causing rotated textures to be placed wrong. + +Build 1.5.1-7.7.1.624: + Christian: + Updated FML: + MinecraftForge/FML@8b8837c9ff635d4988e0a1504fca38667825daf0 Fix NPE when branding isn't present + +Build 1.5.1-7.7.1.623: + kraphteu: Remove unneeded SideOnly. ref: Buildcraft/Buildcraft#710 + +Build 1.5.1-7.7.1.622: + Christian: + Removed obsolete patches + Updated FML: + MinecraftForge/FML@26ccb9106e443e664b0fdc1b95c3600b90ab2bc5 Change snooper/crash report brand handling. fmlbranding now loads another string! + MinecraftForge/FML@eff464cf0e656d92dfedf16e79e5cd6c36b9fc76 And the core patches for that + +Build 1.5.1-7.7.1.621: + Christian: Sanity check the item ID for loaded liquid stacks better. + +Build 1.5.1-7.7.1.620: + Christian: Make liquidstacks immutable in their type data. Sorry about this, but it makes things a lot easier. + Christian: Add in persistence code for LiquidTank. Using it should protect against liquids that get removed f.e. Closes #395 + +Build 1.5.1-7.7.1.618: + Christian: Fix up liquidstack so it knows about the texture sheet for it's icon + +Build 1.5.1-7.7.1.617: + Christian: Fix AT file + +Build 1.5.1-7.7.1.616: + Christian: Fix possible NPE in ChunkManager. Closes #478 in reality + +Build 1.5.1-7.7.1.615: + Christian: made BlockFluid.theIcon protected + Christian: fix bug #489 from #429 + Christian: Fix up some liquid logic + +Build 1.5.1-7.7.1.614: + Christian: + Updated FML: + MinecraftForge/FML@2bc6a0666f8a54f6b1fbc3398c6e3a385ecd35b5 Attempt to make any "default package" class public. This might fix problems with certain modloader mods that ship changes to vanilla classes. + Christian: Use the liquid name in liquid stack persistence. Closes #429 + Christian: Fix hashCode + Christian: + Updated FML: + MinecraftForge/FML@0d844874124649099dbcbb9ae2b36719e1dda25f Fix up derp in access transformer + +Build 1.5.1-7.7.1.611: + LexManos: Bump version number for 1.5.1 Note: We really need to do this more... + +Build 1.5.1-7.7.0.610: + LexManos: Fix NPEs when modders stupidly register null texture names. + +Build 1.5.1-7.7.0.609: + LexManos: Fixed issue with Event.hasAnnotation ignoring it's parameter. And cached its values for potential performance gain. Closes #482 + +Build 1.5.1-7.7.0.608: + jesse: + Entity Extended Properties Changes + + Adds IExtendedEntityProperties interface, which specifies three methods + that are needed: Init, Save, and Load. + + Adds the EntityConstructing event, which is called during the + constructor of Entity. It is needed there so that the reference is in + place during the ReadNBT call. + + Adds hooks into Entity that allow registration of + IExtendedEntityProperties classes, as well as saving and loading to NBT. + jesse: + Brewing Stand Changes + + Added an event on potion ingredient applied. Event contains the item + stacks of each of the potions being brewed as well as any remaining + ingredients. + + Changed TileEntityBrewingStand and SlotBrewingStandPotion to look for + instanceof ItemPotion rather than potion.itemID + jesse: + Player Flyable Fall Event + + Adds an event to EntityPlayer that is posted on player fall when the + player has flight capabilities. + Christian: Fix itemframe render bug + Christian: Add in block rotation support. It supports most vanilla blocks (hopefully), logs should be added soon. + jesse: + Access Transformer Changes + + AT changes and corresponding class changes. + Additions (all made public): + EntityLiving.targetTasks + PotionHelper.potionRequirements + PotionHelper.potionAmplifiers + PotionEffect.duration + Potion.setIconIndex + Item.setPotionEffect + Block.blockHardness + Block.blockResistance + jesse: + Enderman Teleport Event + + New event when an enderman teleports that allows the teleport location + to either be modified or completely cancelled. + jesse: + Enderman attackEntityFrom changed + + Changed so that if the teleport fails upon being attacked, Endermen will + take damage as normal. + jesse: + Ender Teleport Changes + + Renamed Ender Teleport Event and added it in to ender pearls. + jesse: + quick bugfix in entityenderman + + reversed boolean check on event result + Christian: + Updated FML: + MinecraftForge/FML@2d368c4c2eb6b4e0bb60757b6e1679c23aaad9db Itemstacks in the GameRegistry (manual registration by mods) + Christian: + Updated FML: + MinecraftForge/FML@5e1949eb0e3a43d3ec6c710289532d93f6819934 Clone the itemstack, and allow for passing in a stacksize + Christian: + Updated FML: + MinecraftForge/FML@ede93d438f2b5fab92cd6a459247ca158354e430 Validate supplied stack size + MinecraftForge/FML@d73ac867df775174dafdd2da193fabd7c3e7407e Add a mechanism to dump the registry- useful for mod developers. + Christian: + Updated FML: + MinecraftForge/FML@82bc9f1b35f5d51a722d294dd252e6bab4d54fa4 Add some information, and a log message letting you know it worked + Christian: Update patches! + Christian: Re-enable changelog? Hopefully... + Christian: Changelog fixup + Christian: Damn you python and your tab obsession! + +Build 1.5.1-7.7.0.605: + Christian: Fix AT config for texturemap + +Build 1.5.1-7.7.0.604: + Christian: + Allow itemstack sensitive damage computation for attacks. Should allow + storing data in the nbt :) + +Build 1.5.1-7.7.0.603: + Christian: + Updated FML: + MinecraftForge/FML@d9db27275ea06d37ae75e201140019ca152314b0 Fix mismatch screen a bit. Should render more correctly now and at least not be blank in a lot of circumstances.. + Christian: Allow items to decide if they can or cannot have book enchantments applied + +Build 1.5.1-7.7.0.602: + Christian: + Updated FML: + MinecraftForge/FML@d1ff1967b50f2ff2edf0b60acdb6508c0a3eeb06 Fixed FMLRelaunchLog's Newline Handling + MinecraftForge/FML@591a25722b36d2d1b200a31278cb8da117363f6b Merge pull request #208 from jrtc27/patch-1 + +Build 1.5.1-7.7.0.601: + Christian: + Updated FML: + MinecraftForge/FML@f709ce757984b13acb7208d6d8fbdeaa83d2928d Fix missing block world rendering + MinecraftForge/FML@748eece456e079a21112a77047d004d4f410b170 Merge pull request #206 from mDiyo/patch-1 + MinecraftForge/FML@012a755bb9ce9737c843b0dfd86473d143b637be Fix up patch properly. Also, fix md5s for the jars. + +Build 1.5.1-7.7.0.600: + LexManos: + Update to 1.5.1 Pre-Release: + MinecraftForge/FML@9565529baf77de27ed8b75be2065da3ba08d16c8 Updated to latest MCP and Minecraft 1.5.1 Pre-release. + MinecraftForge/FML@a573faf92def5dd01af380b3ca86de877c1178a2 Someone derped up this function bad, revert name. + LexManos: MinecraftForge/FML@aaf02ea2ac938d8cbaafdd2f59985c0604d648d7 Updated for new PR client jar. + +Build 1.5-7.7.0.598: + LexManos: + Updated FML: + MinecraftForge/FML@3c346247e1c5de12d4548f6a99349157057e2de6 Fix NPE with CoreMods who do not have the new MCVersion annotation. + LexManos: + Updated FML: + MinecraftForge/FML@625da6492dddcaca8133718aeee97f9981ced623 Fix install.py --mcp-dir option. Fixes #204. + MinecraftForge/FML@687d3c059d054c338c25a489be206a9f3dc63d81 Merge pull request #204 from agaricusb/fix-mcpdir + +Build 1.5.1-7.7.0.597-1.5.1: + LexManos: MinecraftForge/FML@aaf02ea2ac938d8cbaafdd2f59985c0604d648d7 Updated for new PR client jar. + +Build 1.5-7.7.0.595: + LexManos: Change placement of ChunkDataEvent.Save call to apply before sending to worker thread. + +Build 1.5-7.7.0.594: + Christian: + Updated FML: + MinecraftForge/FML@5cc90f060caace93c0bf041d3cc37208f425f623 Fix the logger to treat newlines better, hopefully. Closes #199 + MinecraftForge/FML@5e3af8ac0e18cfa3a92f9ba726ec1a6b55e87d97 Use the relaunch log, for class circularity safety. + MinecraftForge/FML@ce949e6099fe2a63ee6774acd6e55aa55d3b3673 Support simple mc version test in coremods, only effective through jar loading. Also, log some more data about the environment. + +Build 1.5-7.7.0.593: + LexManos: + New TextureStitchEvents called before and after a TextureMap gathers and stitches textures together. + Also added a config option to diable the writing of resulting stitched textures to disc. Default disables the writing. + +Build 1.5-7.7.0.592: + LexManos: + Updated FML: + MinecraftForge/FML@24022ab6ba79e4babb57fc0db893c23d4aec85bc Added comments to note FML-only methods + MinecraftForge/FML@8905237306230a33e2a3bab7a2b6f7a8b42d94e4 Merge pull request #200 from bspkrs/patch-2 + MinecraftForge/FML@577b19c1cf12a354112e829fb5704c32fd6cd0a5 Fix potential NPE in class loading and add extra debug information. + +Build 1.5-7.7.0.591: + LexManos: Fix placing of certian items on Redstone Blocks. + +Build 1.5-7.7.0.590: + LexManos: Fix equipment slots for items when right clicked, and dispensed using a dispensor. + +Build 1.5-7.7.0.589: + LexManos: Prevent pistons from generating snowballs. + LexManos: Fix logic inversion that allowed SnowMen to create snow on hoppers. + +Build 1.5-7.7.0.588: + lepko.san: Fix ShapedOreRecipe checking mirrored recipes + +Build 1.5-7.7.0.587: + Christian: Fix multipass item rendering so that it uses the right spritesheet for the item. + +Build 1.5-7.7.0.586: + froggytheturtle: + Made WorldServer.allPlayersSleeping public + + Made EntityPlayer.sleepTimer public + + Sleep changes + +Build 1.5-7.7.0.585: + LexManos: Fix Activator rails activating TNT carts, closes #458 + +Build 1.5-7.7.0.584: + github: Update ForgeDummyContainer.java + +Build 1.5-7.7.0.583: + Christian: + Updated FML: + MinecraftForge/FML@c5d5f4e5164111c5ae63e8de7ce97cc583d73e6e Fix AllPublic access transformer not affecting methods. Should fix modloader compatibility + +Build 1.5-7.7.0.582: + LexManos: + Updated FML: + MinecraftForge/FML@4762d4d8ef00bd789ffb6bccbd12f7478b07da62 Allocate more ram {typically 256 is defailt} to fermflower, should fix decomplication issues on OSX + MinecraftForge/FML@6370c242f0e1cb8ec80c7dccc1133cb0d0607bae OS X's python 2.6.1 has a bug in zipfile.extractall that makes it unzip directories as regular files. So switch to extract + +Build 1.5-7.7.0.581: + LexManos: Remove the block if TE errors. + +Build 1.5-7.7.0.580: + LexManos: Add config toggle to atempt to remove TileEntities and Entities that error during there update without fully crashing the server, use at your own risk. Closes #424 + +Build 1.5-7.7.0.579: + Christian: + Updated FML: + MinecraftForge/FML@8f2dbf7046f52d836993edb946d7d310b399bf9d Fix up stupid derp in IMC code: actually reset the IMC list after each delivery. Fixes a bunch of mods. Sorry everyone. + +Build 1.5-7.7.0.578: + ohai.iChun: + [Bugfix] One should not assume an item would use the item spritesheet. + + Item class has a func to return an int to use terrain.png or items.png. This makes forge take account of it. + +Build 1.5-7.7.0.577: + LexManos: Fix bug with rendering one too many passes for ItemEntities Closes #450 + LexManos: Fix RedstoneBlock power issues, Closes #452 + LexManos: Fix for nether quartz not generating in the nether, Closes #454 + +Build 1.5-7.7.0.576: + LexManos: Deprecation Sweep in DungeonHooks and fixed wildcard in ChestGenHooks remove function. + +Build 1.5-7.7.0.575: + LexManos: Fix RenderItem to work with items that use the terrain texture map that aren't in the block ID range. Close #443 + LexManos: Move the RenderWorldLastEvent back to before renderHand like it was in 1.4 Closes #444 + LexManos: Add call to EntityLiving when counting entities for Spawning Cap. Closes #447 + LexManos: Fix missed wildcard change in OreDictionary closes #448 + +Build 1.5-7.7.0.574: + LexManos: Fix crash when EnumHelper can't find $VALUES field, log info, and return gracefully. + +Build 1.5-7.7.0.573: + Christian: + Updated FML: + MinecraftForge/FML@23ea835fa7bc0cdb466d058814b5a0e0c67e8c9a Pass obfuscation status to coremods + +Build 1.5-7.7.0.572: + Christian: Some tweaks to the liquid dictionary, to allow for canonical liquid stacks for things like rendering + Christian: + Updated FML: + MinecraftForge/FML@d88db6c0cfd5484428b574889eae02d34535beae Fix up deep tree deobfuscation + +Build 1.5-7.7.0.571: + LexManos: Removed get/setTextureFile from Block, nolonger used. + LexManos: + Updated FML: + MinecraftForge/FML@7b722bfcd6d4c6867d15492c293a455dfd50d272 Update MCP for latest PR silent update. + MinecraftForge/FML@c6dab815f4e036e25b8f56bef7b8ee63f838adb4 Missed joined.exc, must fix scripts. + +Build 1.5-7.7.0.569: + LexManos: + Updated FML: + MinecraftForge/FML@a90504315e928915345c7b04972d912cdaa0bfdb Readjust size of mods button when Minecraft Realms button is enabled. + +Build 1.5-7.7.0.568: + Christian: Fix the oredictionary for the new recipe wildcard value of Short.MAX_VALUE. + +Build 1.5-7.7.0.567: + Christian: + Updated FML: + MinecraftForge/FML@3765ceb02d783ae5156976f3165bafdb6a3ddbb3 Update MCP, fixes the "broken texture packs" problem. + +Build 1.5-7.7.0.566: + Christian: + Updated FML: + MinecraftForge/FML@179c504746910d4196eef3ee2d56f63cf585c983 Simplify logic in tick start/end + MinecraftForge/FML@29edd242cd7a1fadedf4fb874ea8bbd4e643bffa Fix coremods without a manifest crashing the game. Closes #181 + MinecraftForge/FML@ac16845fc4661fa046a252eda7f9a9a847940189 Fix demo mode crash. Closes #187 + MinecraftForge/FML@984291cee91f585a6f4300eedfed882c814843f8 Fix supertype parsing to handle null superclass (Hi Object!). Closes #160 + MinecraftForge/FML@f6479299936f0f94cfc43210dd9dd44b8b5350ef Merge branch 'master' of github.com:Uristqwerty/FML + MinecraftForge/FML@b301e8e4c1877be246fd4f0b45085b70773d8f2b Change type of connection queue to a concurrent linked queue. Much more efficient, hopefully. Closes #189 + Christian: + Updated FML: + MinecraftForge/FML@dab22f5b74f3f2a410e20583f811605dc8e3c05f Fix "0 mods" display when installed in forge. + +Build 1.5-7.7.0.565: + Christian: + Updated FML: + MinecraftForge/FML@485db6be2e6b54a9a523a2b06e0d886792b0826a Use the reobfuscation maps in the reflection helper for field lookups: should help some reflection cases with the deobf. + Christian: + Updated FML: + MinecraftForge/FML@591e65fa1aa52d2a72dc527ad1c2ac53c8eb94c4 Revert "Use the reobfuscation maps in the reflection helper for field lookups: should help some reflection cases with the deobf." + MinecraftForge/FML@2a779ec3289f695b477ec6b0822a27801e2deba1 Try a different way of remapping the fields. Should work because it's userspace, not relauncher space + MinecraftForge/FML@ca2d8bd83475f37946b86cf6fabd8ff810f9c2bf Fix reflection helper: it needs to unmap the classname to find the field maps. + +Build 1.5-7.7.0.563: + Christian: + Updated FML: + MinecraftForge/FML@25f3fcad4654d19637878bdfb2b70a9586fb3fc9 Fix up some relauncher stuff: the vanilla applet works now, as do other applets. Deobf data is resolveable for them too. + +Build 1.5-7.7.0.562: + LexManos: + Updated FML: + MinecraftForge/FML@6bf7c9878cc959d5f5fa8ec0bf9d0d75037df882 Fixed srg name of minecraftDir for runtime deobf. + +Build 1.5-7.7.0.561: + Christian: Refresh patch + Christian: + Updated FML: + MinecraftForge/FML@95d0ff18cdca3b5a91b648c847c00f559f8ce6f2 Fix runtime deobfuscation for remapped inner classes + +Build 1.5-7.7.0.560: + LexManos: + Updated FML: + MinecraftForge/FML@86a9c7d35953296f7c8bd3a2b1b43115ef0f9308 Fixup reobfusication of server code if present. + MinecraftForge/FML@8e7956397dd80902f7ca69c466e833047dfa5010 Just enable server side compile, and warn not to complain tous. + MinecraftForge/FML@889efc1c0a9216b55f6de275e4f4a279d977e60c Fixes GameRegistry.registerBlock + MinecraftForge/FML@fe1623a36a1bb8b0a046d833e896fd46d88898ef Merge pull request #195 from RainWarrior/snapshot15 + MinecraftForge/FML@62f5adf8e21d59408af409a88b2c81757fd3c587 Revert "Fix modlist to use the new texture binding functions, i think" + MinecraftForge/FML@58ee06ea8edf508daa4ab3920790c0153cf6660d Some fixes for the snapshot + MinecraftForge/FML@368a2245ef0071b0b7a35d3bd78ab1ae379f8faf Merge branch 'snapshot15' + MinecraftForge/FML@1eba1dfdc00edf12ca3d8586dc342563218fc717 Fix accidental commands.patch overwrite + MinecraftForge/FML@ebdb166ec87e63503f0071e557cdb44629a0e0c2 Merge branch 'snapshot15' + MinecraftForge/FML@450dd8313c2e9e46d173bbd242f84d48266af7c8 Fix up some small things, merging into mainline + MinecraftForge/FML@1642bad402efe819f4e763bf4b460d8c04194849 Fix Multi-part entity children ID issue, mobs with custom spawning must deal with child ids themselves. + +Build 1.5-7.7.0.559: + LexManos: Update patche for jad-style names. + LexManos: Updated FML and at config for 1.5 snapshot + LexManos: First patches updae to 1.5, Many rendeirng related changes, most notibly removed Item/Block's getTextureFile() functions. + LexManos: Removed some dead code, We don't bind custom tessellators + CovertJaguar: MCL Update + LexManos: Bump major and minor version numbers to mark 1.5, it's gunna break everything. Should be a compileable 1.5 build. + Christian: + Fix a couple of forge patches + + Update FML: d075daf + d075daf Merge branch 'master' into snapshot15 Fix up compilation and patching errors + 1bd6847 Fix up packages.csv ordering for easier diffing + dd832f2 Update for MCP7.30c - fixes redstone rendering issues + aebf6eb Add in a registry method to allow for alternative TileEntity names- they will be used to support loading maps containing the older definiti + 8921cfe Remember to add the new patches! + cd67596 The "ServerStarting" event should now properly crash the server if it fails, so the client will properly exit. Also, added in a "pre-server + e1c6630 Javadoc cleanup + 5ce4e31 Fix breaking change + a99c488 Merge branch 'patch-1' of https://github.com/bspkrs/FML into gh-updates + 94282c5 Merge branch 'FMLLogFormatter' of https://github.com/donington/FML into gh-updates + 7ad8529 Update MCP to MCP7.26a and refresh MCP names + 15534ed Update address of the FML repository to the new location in all the files + aa822e3 Fix logical error in comment text + a14ab91 Update license text to make clear that FML is not a way to sidestep MCP licensing. + 0165742 Fix mcp conf md5 signatures for the snapshot + 26a5b31 FMLLogFormatter: dynamic log level name + LexManos: + Merge commit '695b080197bd577cc34fe6dbc72b74f4a74b2d5c' into snapshot15 + Testing cherry picking. + LexManos: + Sync up with FML, Will not run nativly as you need deobfusication_data.zip in your libs folder. + We have not setup the download for that yet, to make it yourself just zip joined.srg name it deobfusication_data.zip and put it in lib + LexManos: Not supposed to have debug stuff... + LexManos: Updated Forge to s13w09c + Christian: Fix up patches for FML, also, add in FML as a submodule rather than a zip + Christian: + Add in simple texture management for mods using the stitcher. Scope with "{domain}:{texture}" to + get textures that are not at /textures//{texture}.png but /mods/{domain}/textures//{texture}.png + instead + LexManos: Testing selective commit of submodule. + LexManos: + Added submodule changelog ganerator: + + Updated FML: + MinecraftForge/FML@e74087ee430633475c3ca058e54e3ef242a9d6aa Ignore again, testing submodule. + LexManos: Removed GNUWin32 files and uneeded files seince FML is now a submodule. + LexManos: Remove window helper batch files. + LexManos: Update python scripts to reflect that FML is now a submodule. Delete updateasmdata as it's in /fml/ now. Build should work once again. + LexManos: Change FML module to read-only connection. + Christian: Update submodule + LexManos: Copy over some needed files for debugging. + LexManos: Rework configuration, configs should now use Config.hasChanged to deterne if thehould call save(), also re-worked the saving to not use String.format as much. + LexManos: Fixed new python changes. + LexManos: + Updated FML: + MinecraftForge/FML@aed2cc446ad8d5882890c5f218eb894ea7bd2577 Force file name encoding to UTF-8, caused different zips on different systems. + LexManos: Fix animation location for textures with domains + LexManos: Add helper functions for deling with custom TextureStitched + LexManos: Add callback on TextureStitched to control texture loading. + LexManos: New world event for controlling potential entity spawnlists. For #430 + LexManos: Removed erroring imports and update build function to die on errors. + LexManos: Fix ItemSeedFood respecting custom soils. + LexManos: + Updated FML: + MinecraftForge/FML@debbdc00be8ea1a261cdff83785ddc7100419a74 Capture Minecraft logs into FML logging + MinecraftForge/FML@74fffc6fdc2eda8caa9a7feb0826d7babb84751a Update next render to 40 + Christian: + Call stitcher for non-existent textures as well. Probably allows + for generated textures. Hmmm + Christian: let's do it right this time. Hmmm + Christian: + Updated FML: + MinecraftForge/FML@abe4f73a9a3158f6f9d1ea2334798f54a25817bf FIX massive performance issue with FML. Thanks to @sfPlayer1 for finding this epic derp on my part! + Christian: Add an Icon to the LiquidStack, for rendering the liquid in various ways + LexManos: Readd second render pass to TileEntities and Entities, patches were missed when merging in master branch. As note, CB can DIAF. + LexManos: Fix compile error, forget to flush to disc. + LexManos: Make release quit on compile error. + Christian: Clean up Access Transformer mapping data + Christian: + Updated FML: + MinecraftForge/FML@e9ff699c2dcd787a3e0ebaa427c625a48de4c9fb Refresh the renderengine after modloading is complete. Should fix issues with out-of-place texture registration by mods. + MinecraftForge/FML@a723aa68606d57b0ee5bac8b1d1905abef440b54 Refresh copyright notices on everything + MinecraftForge/FML@c42a2101408b21799728c88e2d02c718c3b0dd36 Strip deprecated code + MinecraftForge/FML@6eeae8c49ff4359dc21c44eb73e4e043285cd8bf Fix up state transition derp when the server crashes with an error- it shouldn't double-derp + MinecraftForge/FML@81c6421f84c1bff359dfe927974e8730b348806a Tweak license text- any osi licensed project can use the asm transformer code + LexManos: + Updated to 1.5 Pre-release + Updated FML: + MinecraftForge/FML@2d98835db8c6a7665ef55117d60ab4318876836b Scala support! It's still primitive, I hope that people will like it. I do :) + MinecraftForge/FML@5bfaf7c1700191b6ed8f4752c9a95bf8c25323ef Global object registry, also, support the new itemblockwithmetadata constructor + MinecraftForge/FML@80a40c03e644840d827eb7d67ff97f6558eaa2e4 Update to MCP 1.5 and minecraft 1.5. + MinecraftForge/FML@b3e854a15d7c50b4967be8237df5fdace95a15ee Update for new MCP with srg reobf. + LexManos: Fix typos. + LexManos: Deprecated Forge's ISidedInventroy, there is a vanilla solution. Added temporary config option to legacy furnace slot orientation. + LexManos: + Updated FML: + MinecraftForge/FML@f1c6bdd57d41a938cb3326d509042f6842e42396 Support the MCP format of partial reobfuscation for portability. Ensure modloader-like compatibility + MinecraftForge/FML@0419b9d9751ade4497343aefaf2ca43703eb479a Update MCP info for latest + LexManos: Early define CrashReport classes to combat invalid crash details. + LexManos: Fix reobf call. + +Build 1.5-7.7.0.558-snapshot15: + Christian: Clean up Access Transformer mapping data + Christian: + Updated FML: + MinecraftForge/FML@e9ff699c2dcd787a3e0ebaa427c625a48de4c9fb Refresh the renderengine after modloading is complete. Should fix issues with out-of-place texture registration by mods. + MinecraftForge/FML@a723aa68606d57b0ee5bac8b1d1905abef440b54 Refresh copyright notices on everything + MinecraftForge/FML@c42a2101408b21799728c88e2d02c718c3b0dd36 Strip deprecated code + MinecraftForge/FML@6eeae8c49ff4359dc21c44eb73e4e043285cd8bf Fix up state transition derp when the server crashes with an error- it shouldn't double-derp + MinecraftForge/FML@81c6421f84c1bff359dfe927974e8730b348806a Tweak license text- any osi licensed project can use the asm transformer code + LexManos: + Updated to 1.5 Pre-release + Updated FML: + MinecraftForge/FML@2d98835db8c6a7665ef55117d60ab4318876836b Scala support! It's still primitive, I hope that people will like it. I do :) + MinecraftForge/FML@5bfaf7c1700191b6ed8f4752c9a95bf8c25323ef Global object registry, also, support the new itemblockwithmetadata constructor + MinecraftForge/FML@80a40c03e644840d827eb7d67ff97f6558eaa2e4 Update to MCP 1.5 and minecraft 1.5. + MinecraftForge/FML@b3e854a15d7c50b4967be8237df5fdace95a15ee Update for new MCP with srg reobf. + LexManos: Fix typos. + LexManos: Deprecated Forge's ISidedInventroy, there is a vanilla solution. Added temporary config option to legacy furnace slot orientation. + LexManos: + Updated FML: + MinecraftForge/FML@f1c6bdd57d41a938cb3326d509042f6842e42396 Support the MCP format of partial reobfuscation for portability. Ensure modloader-like compatibility + MinecraftForge/FML@0419b9d9751ade4497343aefaf2ca43703eb479a Update MCP info for latest + LexManos: Early define CrashReport classes to combat invalid crash details. + LexManos: Fix reobf call. + +Build 13w09c-7.7.0.556-snapshot15: + LexManos: Make release quit on compile error. + +Build 13w09c-7.7.0.555-snapshot15: + LexManos: Readd second render pass to TileEntities and Entities, patches were missed when merging in master branch. As note, CB can DIAF. + LexManos: Fix compile error, forget to flush to disc. + +Build 13w09c-7.7.0.553-snapshot15: + Christian: Add an Icon to the LiquidStack, for rendering the liquid in various ways + +Build 13w09c-7.7.0.552-snapshot15: + Christian: + Updated FML: + MinecraftForge/FML@abe4f73a9a3158f6f9d1ea2334798f54a25817bf FIX massive performance issue with FML. Thanks to @sfPlayer1 for finding this epic derp on my part! + +Build 13w09c-7.7.0.551-snapshot15: + Christian: let's do it right this time. Hmmm + +Build 13w09c-7.7.0.550-snapshot15: + Christian: + Call stitcher for non-existent textures as well. Probably allows + for generated textures. Hmmm + +Build 13w09c-7.7.0.549-snapshot15: + LexManos: Fix ItemSeedFood respecting custom soils. + LexManos: + Updated FML: + MinecraftForge/FML@debbdc00be8ea1a261cdff83785ddc7100419a74 Capture Minecraft logs into FML logging + MinecraftForge/FML@74fffc6fdc2eda8caa9a7feb0826d7babb84751a Update next render to 40 + +Build 13w09c-7.7.0.548-snapshot15: + LexManos: Removed erroring imports and update build function to die on errors. + +Build 13w09c-7.7.0.547-snapshot15: + LexManos: New world event for controlling potential entity spawnlists. For #430 + +Build 13w09c-7.7.0.546-snapshot15: + LexManos: Add callback on TextureStitched to control texture loading. + +Build 13w09c-7.7.0.545-snapshot15: + LexManos: Fix animation location for textures with domains + LexManos: Add helper functions for deling with custom TextureStitched + +Build 13w09c-7.7.0.544-snapshot15: + LexManos: Copy over some needed files for debugging. + LexManos: Rework configuration, configs should now use Config.hasChanged to deterne if thehould call save(), also re-worked the saving to not use String.format as much. + LexManos: Fixed new python changes. + LexManos: + Updated FML: + MinecraftForge/FML@aed2cc446ad8d5882890c5f218eb894ea7bd2577 Force file name encoding to UTF-8, caused different zips on different systems. + +Build 13w09c-7.7.0.543-snapshot15: + Christian: Update submodule + +Build 13w09c-7.7.0.542-snapshot15: + Christian: Fix up patches for FML, also, add in FML as a submodule rather than a zip + Christian: + Add in simple texture management for mods using the stitcher. Scope with "{domain}:{texture}" to + get textures that are not at /textures//{texture}.png but /mods/{domain}/textures//{texture}.png + instead + LexManos: Testing selective commit of submodule. + LexManos: + Added submodule changelog ganerator: + + Updated FML: + MinecraftForge/FML@e74087ee430633475c3ca058e54e3ef242a9d6aa Ignore again, testing submodule. + LexManos: Removed GNUWin32 files and uneeded files seince FML is now a submodule. + LexManos: Remove window helper batch files. + LexManos: Update python scripts to reflect that FML is now a submodule. Delete updateasmdata as it's in /fml/ now. Build should work once again. + LexManos: Change FML module to read-only connection. + +Build 13w09c-7.7.0.539-snapshot15: + LexManos: Updated Forge to s13w09c + +Build 13w05b-7.7.0.538-snapshot15: + LexManos: Not supposed to have debug stuff... + +Build 13w05b-7.7.0.537-snapshot15: + LexManos: + Sync up with FML, Will not run nativly as you need deobfusication_data.zip in your libs folder. + We have not setup the download for that yet, to make it yourself just zip joined.srg name it deobfusication_data.zip and put it in lib + +Build 13w02b-7.7.0.536-snapshot15: + mehvids: Gave entities and tile entities access to the second render pass for translucency. + Christian: Attempt to fix a possible NPE in the face of ChickenBones' hackery. ChickenBones. stop it! + Christian: + Fix a potential problem with "Entity already added" when using the dormant + chunk cache capability. The entities in the dormant chunk cache will get new + IDs prior to the cached chunk returning. + Christian: Fix NPE causing issue with the cache. Derpy derp. + Christian: + Add in TESR culling, and a new TileEntity method to allow for differential sizing of the + TESR view culling vs the TE collision bounding box (the former defaults to the latter) + Checked into a branch because it's likely to break expanded TileEntities. + Christian: Bump the revision number for the TESR and renderpass changes + Christian: + Add in a mechanism for explicit subclassing of WeightedRandomChestItem to allow for + generational style chest content generation rather than static. Cleans up some old code nicely + Christian: + We try and log a message if we detect a world leak: it's probably not infallible, but it should + help mod developers- if you see this when testing your mod with, say, mystcraft, you're probably + keeping a hold of an invalid handle to the World (either directly, or indirectly via Entity or TileEntity) + and you should look to refactor to wrap those handles in WeakReferences + LexManos: Moved warning logic down, so that no more tickets are isues if the mod is over it's alotment. Fixes #378 + LexManos: Forge Additions: Exposed ChunkCache.worldObj to public PR #383 + LexManos: Fixup a resource leak warning. + LexManos: Add DimensionManager.unregisterProviderType for PR #388 + LexManos: Added input getters for Ore recipies, and javadoc warning for modders, #390 + LexManos: Rework canSilkHarvest hook to try and honor vanilla overrides, should close #391 + LexManos: Added catch to TileEntityChestRenderer for potential crash when modders do bad things -.- Closes #389 + LexManos: Change access of upper and lower chest fields of InventoryLargeChest to public. Closes #387 + LexManos: Change WorldServer.allPlayersSleeping to public, and remove the SideOnly annotation on EntityPlayer.getSleepTimer() Closes #393 + LexManos: Fix initalization issue with the clamping threshold config value. And remove vanilla console spam related to it. + Christian: Fix small derp in TE + Christian: + Update FML:549b6fd + 549b6fd IMC tweaks: runtimeMessages now work (thanks for all that testing for this much requested feature!) and IMCEvent will no longer rem + 9fafdc1 More logging tweaks. You can probably configure individual mod log files if you wish now + f169f7c A log of logging cleanup. FML will now read logging.properties to configure logging channels a couple of times during startup. You + 3ac891f Try and handle "death on startup" a bit cleaner + 2dc0189 Deprecate the old GUI ticktype. They're dead and have been for some time. + dd98784 Tweak a method signature + 1c9a510 Add parameters to FML install to enable/disable certian aspects. Applying patches, running transformer/merger, and decompiling serv + 1bd6847 Fix up packages.csv ordering for easier diffing + Christian: + Add in "armor ticking"- implement the interface and the armor piece will tick. + + Update FML:22dbe41 + 22dbe41 Fix up mistake that broke all modloading. Nice. + Christian: Fix TESR rendering for double chests + Christian: Fix possible null case for collision bounding box. + Christian: Remove the single use interface ITickingArmor, and apply to Item directly. + Christian: + Fix ServerBrand retriever- forge is now forge,fml! + + Update FML: cd96718 + cd96718 Fix HD mob skins, FINALLY!!! Stupid eyes are stupid. + LexManos: Kill generated timestamp in config files, if you want to retreive this information, usethe file's modified time. Closes #404 + LexManos: Actually use the line parameter in ServerChatEvent, closes #401 + LexManos: Kill .sh wrappers until someone writes more robust versions, closes #392 and closes #402 + LexManos: Made LiquidStack.isLiquidEqual(ItemStack) properly check the contained liquid, in addition to obvious id/meta closes #399 and closes #403 + LexManos: Add item frame transformations to the EntityItem render helper Closes #407 + LexManos: Added a hook into SlotArmor so Items can control if they are classified as a Armor type. Closes #408 + LexManos: Fixes comparison for items in creative inventory, closes #411 + LexManos: Fix fortune modifier always passed as 0 to idDropped closes #412 + LexManos: Fix EventTransformer throwing an NPE when transforming a class that doesn't exist. Closes #413 + LexManos: Fix profiler issue with RenderGlobal patch. Closes #414 + LexManos: Jenkins needs this script, *pokes Overmind* -.- + LexManos: + Update FML to 556: + b6d6f235 Fix sprite map issue. + 1158aa46 Fix Language Registry, closes #FML 163 + 50ce6fb3 Option to disable renaming for srgnames. + LexManos: Disable automatic equiti of pcke dup items for players, and fixed index issues in Player.setCurrentItemOrArmor. + LexManos: Fixed entity colision above max world height, and below 0. Closes #400 + LexManos: Change usage of ketSet/get to entrySet in OreDictionary, closes #422 + LexManos: Fix movement speed check, closes #420 + LexManos: clarify what the size is measured in for the dormant chunk cache. -.- + LexManos: ItemStack sensitive versions of Item.getPotionEffect and Item.isPotionIngredient Closes #321 + LexManos: WorldGen*Trees shoud not respect custom soils. Closes #355 + LexManos: Added unload event for client worlds Closes #405 + LexManos: Store the glMultiTexCoord lightmap for later use with glDrawArrays closes #406 + LexManos: Add water and lava to liquid dictionary by default, closes #419 + LexManos: Bump version to 6.6.2, declaring Forge for MC 1.4.7 feature complete, as 1.5 is on the horizon, only bug fixes from this point on. Unless something major happens on Mojang's end. + LexManos: Fix TESR culling for beacons, and implement a good enough measure for Chests. + +Build 13w02b-7.7.0.535-snapshot15: + LexManos: + Merge commit '695b080197bd577cc34fe6dbc72b74f4a74b2d5c' into snapshot15 + Testing cherry picking. + +Build 1.4.7-6.6.2.534: + LexManos: Fix TESR culling for beacons, and implement a good enough measure for Chests. + +Build 1.4.7-6.6.2.533: + LexManos: Bump version to 6.6.2, declaring Forge for MC 1.4.7 feature complete, as 1.5 is on the horizon, only bug fixes from this point on. Unless something major happens on Mojang's end. + +Build 1.4.7-6.6.1.532: + LexManos: Added unload event for client worlds Closes #405 + LexManos: Store the glMultiTexCoord lightmap for later use with glDrawArrays closes #406 + LexManos: Add water and lava to liquid dictionary by default, closes #419 + +Build 1.4.7-6.6.1.531: + LexManos: WorldGen*Trees shoud not respect custom soils. Closes #355 + +Build 1.4.7-6.6.1.530: + LexManos: Fix movement speed check, closes #420 + LexManos: clarify what the size is measured in for the dormant chunk cache. -.- + LexManos: ItemStack sensitive versions of Item.getPotionEffect and Item.isPotionIngredient Closes #321 + +Build 1.4.7-6.6.1.529: + LexManos: Disable automatic equiti of pcke dup items for players, and fixed index issues in Player.setCurrentItemOrArmor. + LexManos: Fixed entity colision above max world height, and below 0. Closes #400 + LexManos: Change usage of ketSet/get to entrySet in OreDictionary, closes #422 + +Build 1.4.7-6.6.1.528: + LexManos: + Update FML to 556: + b6d6f235 Fix sprite map issue. + 1158aa46 Fix Language Registry, closes #FML 163 + 50ce6fb3 Option to disable renaming for srgnames. + +Build 1.4.7-6.6.1.527: + LexManos: Kill generated timestamp in config files, if you want to retreive this information, usethe file's modified time. Closes #404 + LexManos: Actually use the line parameter in ServerChatEvent, closes #401 + LexManos: Kill .sh wrappers until someone writes more robust versions, closes #392 and closes #402 + LexManos: Made LiquidStack.isLiquidEqual(ItemStack) properly check the contained liquid, in addition to obvious id/meta closes #399 and closes #403 + LexManos: Add item frame transformations to the EntityItem render helper Closes #407 + LexManos: Added a hook into SlotArmor so Items can control if they are classified as a Armor type. Closes #408 + LexManos: Fixes comparison for items in creative inventory, closes #411 + LexManos: Fix fortune modifier always passed as 0 to idDropped closes #412 + LexManos: Fix EventTransformer throwing an NPE when transforming a class that doesn't exist. Closes #413 + LexManos: Fix profiler issue with RenderGlobal patch. Closes #414 + LexManos: Jenkins needs this script, *pokes Overmind* -.- + +Build 1.4.7-6.6.1.524: + Christian: + Fix ServerBrand retriever- forge is now forge,fml! + + Update FML: cd96718 + cd96718 Fix HD mob skins, FINALLY!!! Stupid eyes are stupid. + +Build 1.4.7-6.6.1.523: + Christian: Remove the single use interface ITickingArmor, and apply to Item directly. + +Build 1.4.7-6.6.1.522: + Christian: Fix possible null case for collision bounding box. + +Build 1.4.7-6.6.1.521: + mehvids: Gave entities and tile entities access to the second render pass for translucency. + Christian: + Add in TESR culling, and a new TileEntity method to allow for differential sizing of the + TESR view culling vs the TE collision bounding box (the former defaults to the latter) + Checked into a branch because it's likely to break expanded TileEntities. + Christian: Bump the revision number for the TESR and renderpass changes + Christian: Fix small derp in TE + Christian: + Update FML:549b6fd + 549b6fd IMC tweaks: runtimeMessages now work (thanks for all that testing for this much requested feature!) and IMCEvent will no longer rem + 9fafdc1 More logging tweaks. You can probably configure individual mod log files if you wish now + f169f7c A log of logging cleanup. FML will now read logging.properties to configure logging channels a couple of times during startup. You + 3ac891f Try and handle "death on startup" a bit cleaner + 2dc0189 Deprecate the old GUI ticktype. They're dead and have been for some time. + dd98784 Tweak a method signature + 1c9a510 Add parameters to FML install to enable/disable certian aspects. Applying patches, running transformer/merger, and decompiling serv + 1bd6847 Fix up packages.csv ordering for easier diffing + Christian: + Add in "armor ticking"- implement the interface and the armor piece will tick. + + Update FML:22dbe41 + 22dbe41 Fix up mistake that broke all modloading. Nice. + Christian: Fix TESR rendering for double chests + +Build 1.4.7-6.6.1.520-TESRculling: + Christian: Fix small derp in TE + +Build 1.4.7-6.6.1.519-TESRculling: + Christian: + Add in a mechanism for explicit subclassing of WeightedRandomChestItem to allow for + generational style chest content generation rather than static. Cleans up some old code nicely + Christian: + We try and log a message if we detect a world leak: it's probably not infallible, but it should + help mod developers- if you see this when testing your mod with, say, mystcraft, you're probably + keeping a hold of an invalid handle to the World (either directly, or indirectly via Entity or TileEntity) + and you should look to refactor to wrap those handles in WeakReferences + LexManos: Moved warning logic down, so that no more tickets are isues if the mod is over it's alotment. Fixes #378 + LexManos: Forge Additions: Exposed ChunkCache.worldObj to public PR #383 + LexManos: Fixup a resource leak warning. + LexManos: Add DimensionManager.unregisterProviderType for PR #388 + LexManos: Added input getters for Ore recipies, and javadoc warning for modders, #390 + LexManos: Rework canSilkHarvest hook to try and honor vanilla overrides, should close #391 + LexManos: Added catch to TileEntityChestRenderer for potential crash when modders do bad things -.- Closes #389 + LexManos: Change access of upper and lower chest fields of InventoryLargeChest to public. Closes #387 + LexManos: Change WorldServer.allPlayersSleeping to public, and remove the SideOnly annotation on EntityPlayer.getSleepTimer() Closes #393 + LexManos: Fix initalization issue with the clamping threshold config value. And remove vanilla console spam related to it. + +Build 1.4.7-6.6.0.518: + LexManos: Fix initalization issue with the clamping threshold config value. And remove vanilla console spam related to it. + +Build 1.4.7-6.6.0.517: + LexManos: Moved warning logic down, so that no more tickets are isues if the mod is over it's alotment. Fixes #378 + LexManos: Forge Additions: Exposed ChunkCache.worldObj to public PR #383 + LexManos: Fixup a resource leak warning. + LexManos: Add DimensionManager.unregisterProviderType for PR #388 + LexManos: Added input getters for Ore recipies, and javadoc warning for modders, #390 + LexManos: Rework canSilkHarvest hook to try and honor vanilla overrides, should close #391 + LexManos: Added catch to TileEntityChestRenderer for potential crash when modders do bad things -.- Closes #389 + LexManos: Change access of upper and lower chest fields of InventoryLargeChest to public. Closes #387 + LexManos: Change WorldServer.allPlayersSleeping to public, and remove the SideOnly annotation on EntityPlayer.getSleepTimer() Closes #393 + +Build 1.4.7-6.6.0.516: + Christian: + We try and log a message if we detect a world leak: it's probably not infallible, but it should + help mod developers- if you see this when testing your mod with, say, mystcraft, you're probably + keeping a hold of an invalid handle to the World (either directly, or indirectly via Entity or TileEntity) + and you should look to refactor to wrap those handles in WeakReferences + +Build 1.4.7-6.6.0.515: + Christian: + Add in a mechanism for explicit subclassing of WeightedRandomChestItem to allow for + generational style chest content generation rather than static. Cleans up some old code nicely + +Build 1.4.7-6.6.1.514-TESRculling: + Christian: Bump the revision number for the TESR and renderpass changes + +Build 1.4.7-6.6.0.513-TESRculling: + mehvids: Gave entities and tile entities access to the second render pass for translucency. + +Build 1.4.7-6.6.0.511: + Christian: Fix NPE causing issue with the cache. Derpy derp. + +Build 1.4.7-6.6.0.510: + Christian: + Fix a potential problem with "Entity already added" when using the dormant + chunk cache capability. The entities in the dormant chunk cache will get new + IDs prior to the cached chunk returning. + +Build 1.4.7-6.6.0.509: + Christian: Attempt to fix a possible NPE in the face of ChickenBones' hackery. ChickenBones. stop it! + +Build 13w02b-7.7.0.508-snapshot15: + scott: Add ability to WorldTypes to display the 'Customize' button and react to it + LexManos: + Update FML: + New scripts for signing jars, and repackging source folders. + Fix LanguageRegistry loading files in UTF-8 format. loadLocalization should work for non-xml in all languages now (assumes UTF-8) + Fix incorrect end length calculation. Closes #161 Thanks BStramke! + LexManos: Fix check in getItem() to allow lowest item ID #361 + CovertJaguar: + Fixed render passes for EntityItems + + Should be < instead of <= + Christian: + Update licencing information to make clear that forge is allowed to redistribute and automatically + download parts of MCP, but this permission is not transitive to people distributing MinecraftForge + source independently of the MinecraftForge project. + + Update MCP to 7.26a and FML: 7ad8529 + 7ad8529 Update MCP to MCP7.26a and refresh MCP names + 15534ed Update address of the FML repository to the new location in all the files + a14ab91 Update license text to make clear that FML is not a way to sidestep MCP licensing. + Christian: + Update FML: a99c488 + a99c488 Merge branch 'patch-1' of https://github.com/bspkrs/FML into gh-updates + 94282c5 Merge branch 'FMLLogFormatter' of https://github.com/donington/FML into gh-updates + aa822e3 Fix logical error in comment text + 26a5b31 FMLLogFormatter: dynamic log level name + Christian: Merge part of PR #375 related to ChunkEvent.Load for the client side + Christian: + Update FML: 5ce4e31 + 5ce4e31 Fix breaking change + Christian: + Add in patch to change how playerinstance sends TE chunk updates. It should always send just the TEs + that changed now, and not "ALL" TEs. Also, added configuration value to change the 64 threshold to a + configurable number + Christian: Some javadoc fixes + Christian: + Change DimensionManager.getCurrentSaveRootDirectory() to try and work even for the new server about to start event + + Update FML:8921cfe + 8921cfe Remember to add the new patches! + cd67596 The "ServerStarting" event should now properly crash the server if it fails, so the client will properly exit. A + Christian: + Update FML:aebf6eb + aebf6eb Add in a registry method to allow for alternative TileEntity names- they will be used to support loading maps containing the older definiti + Christian: + Fix a couple of forge patches + + Update FML: d075daf + d075daf Merge branch 'master' into snapshot15 Fix up compilation and patching errors + 1bd6847 Fix up packages.csv ordering for easier diffing + dd832f2 Update for MCP7.30c - fixes redstone rendering issues + aebf6eb Add in a registry method to allow for alternative TileEntity names- they will be used to support loading maps containing the older definiti + 8921cfe Remember to add the new patches! + cd67596 The "ServerStarting" event should now properly crash the server if it fails, so the client will properly exit. Also, added in a "pre-server + e1c6630 Javadoc cleanup + 5ce4e31 Fix breaking change + a99c488 Merge branch 'patch-1' of https://github.com/bspkrs/FML into gh-updates + 94282c5 Merge branch 'FMLLogFormatter' of https://github.com/donington/FML into gh-updates + 7ad8529 Update MCP to MCP7.26a and refresh MCP names + 15534ed Update address of the FML repository to the new location in all the files + aa822e3 Fix logical error in comment text + a14ab91 Update license text to make clear that FML is not a way to sidestep MCP licensing. + 0165742 Fix mcp conf md5 signatures for the snapshot + 26a5b31 FMLLogFormatter: dynamic log level name + +Build 1.4.7-6.6.0.507: + Christian: + Update FML:aebf6eb + aebf6eb Add in a registry method to allow for alternative TileEntity names- they will be used to support loading maps containing the older definiti + +Build 1.4.7-6.6.0.506: + Christian: + Change DimensionManager.getCurrentSaveRootDirectory() to try and work even for the new server about to start event + + Update FML:8921cfe + 8921cfe Remember to add the new patches! + cd67596 The "ServerStarting" event should now properly crash the server if it fails, so the client will properly exit. A + +Build 1.4.7-6.6.0.505: + Christian: Some javadoc fixes + +Build 1.4.7-6.6.0.504: + Christian: + Update FML: 5ce4e31 + 5ce4e31 Fix breaking change + Christian: + Add in patch to change how playerinstance sends TE chunk updates. It should always send just the TEs + that changed now, and not "ALL" TEs. Also, added configuration value to change the 64 threshold to a + configurable number + +Build 1.4.7-6.6.0.503: + scott: Add ability to WorldTypes to display the 'Customize' button and react to it + CovertJaguar: + Fixed render passes for EntityItems + + Should be < instead of <= + Christian: Merge part of PR #375 related to ChunkEvent.Load for the client side + +Build 1.4.7-6.6.0.502: + Christian: + Update FML: a99c488 + a99c488 Merge branch 'patch-1' of https://github.com/bspkrs/FML into gh-updates + 94282c5 Merge branch 'FMLLogFormatter' of https://github.com/donington/FML into gh-updates + aa822e3 Fix logical error in comment text + 26a5b31 FMLLogFormatter: dynamic log level name + +Build 1.4.7-6.6.0.501: + Christian: + Update licencing information to make clear that forge is allowed to redistribute and automatically + download parts of MCP, but this permission is not transitive to people distributing MinecraftForge + source independently of the MinecraftForge project. + + Update MCP to 7.26a and FML: 7ad8529 + 7ad8529 Update MCP to MCP7.26a and refresh MCP names + 15534ed Update address of the FML repository to the new location in all the files + a14ab91 Update license text to make clear that FML is not a way to sidestep MCP licensing. + +Build 13w02b-7.7.0.500-snapshot15: + LexManos: Bump major and minor version numbers to mark 1.5, it's gunna break everything. Should be a compileable 1.5 build. + +Build 1.4.7-6.6.0.499: + LexManos: + Update FML: + New scripts for signing jars, and repackging source folders. + Fix LanguageRegistry loading files in UTF-8 format. loadLocalization should work for non-xml in all languages now (assumes UTF-8) + Fix incorrect end length calculation. Closes #161 Thanks BStramke! + LexManos: Fix check in getItem() to allow lowest item ID #361 + +Build 1.4.7-6.6.0.497: + LexManos: Added getter for Metadata smelting list, because, why not.. PR: #352 + LexManos: Fixed incorrect lighting in some cases. Closes issue #349 + LexManos: + Added event hooks to control to allow mod control of mob spawning. PR: #337 + Deprecated LivingSpecialSpawnEvent in favor of new LivingSpawnEvent.SpecialSpawn + +Build 1.4.7-6.6.0.496: + LexManos: Fix issue where dungeon loot table had wrong values. + LexManos: Fixed issue where ChunkPriderEvent.InitNoiseField used the wrong sizeY value. + LexManos: Some small code cleanups. + +Build 1.4.7-6.6.0.495: + Christian: + Update FML:6f1b762 + 6f1b762 Move server stopped *after* the server has actually stopped. *sigh* + +Build 1.4.7-6.6.0.494: + Christian: + Update FML:d9bfb29 + d9bfb29 Add in a "server stopped" event + +Build 1.4.7-6.6.0.493: + Christian: + Attempt to resolve the entity concurrency issue, by simply deferring unload + to the next tick + +Build 1.4.7-6.6.0.492: + Christian: Fix noisy exception logging + Christian: + Update FML:6fc7bc4 + 6fc7bc4 Add in some classloader debugging information: use fml.debugClassLoading=true as a system property to track down prob + +Build 1.4.7-6.6.0.491: + Christian: + Fix a missed patch and cleaned up other patches + Rollback a method name change that breaks a lot of mods + Update FML: fb701cd + fb701cd Revert MCP name change for canConnectRedstone - it conflicts with a forge method of the same name and breaks 1 + +Build 1.4.7-6.6.0.490: + Christian: + Update for MC 1.4.7 + Update FML: f7cc50b + +Build 1.4.6-6.5.0.489: + LexManos: Attempt a fix for the new chunk compression changes. + +Build 1.4.6-6.5.0.488: + Christian: + Update FML:1a232cf + 1a232cf Fix multiple GUI containers for ML containers. Sorry ultimatechest that this fix took so long, a bug report at + 853f54b Log if there's a problem reading the class bytes + +Build 1.4.6-6.5.0.487: + LexManos: Fix mobs spawning on inverted slabs/stairs. + +Build 1.4.6-6.5.0.486: + LexManos: Use nanoTime instead of currentTimeMillis for potential performance increase. + LexManos: Fixed order <.< you saw nothing. + +Build 1.4.6-6.5.0.484: + LexManos: Fixed issue with Efficancy enchatment when connected to vanilla servers. + LexManos: Fixed EntityMinecard missed patch, Issue #338 + LexManos: Fixed typo in dungeon loot + +Build 1.4.6-6.5.0.483: + Uristqwerty: + Force parent ListenerListInsts to rebuild. + + Without this change, it is possible (and, in fact, nearly guaranteed) for lists to rebuild endlessly if a parent list is marked as needing a rebuild but never actually read. This change forces the parent list(s) to rebuild as well, resulting in a significant performance increase and smoother framerate due to greatly reduced GC activity. + Christian: Protect deflation with a simple semaphore. Should close #336 + +Build 1.4.6-6.5.0.482: + LexManos: Update FML: Fix NPE in fingerprint loading, and pass expectged fingerprint to FMLFingerprintViolationEvent + +Build 1.4.6-6.5.0.481: + Christian: + Update FML: 7e6456d + 7e6456d Fix a message delivery issue in IMC + 664ebda Some tweaks for signing and ID matching + +Build 1.4.6-6.5.0.480: + LexManos: + Fixes a vanilla bug where the player view would dip when stepping between certain blocks + https://mojang.atlassian.net/browse/MC-1594 + Issue #318, let me know if you notice any issue. + +Build 1.4.6-6.5.0.479: + LexManos: Fixed parameter ordering u.u + +Build 1.4.6-6.5.0.478: + LexManos: + Re-write/Deprecated DungeonHooks loot tables, now uses ChestGenHooks like the rest of the world gen. Also fixes issue #330 by adding in enchanted books. + + Should be API compatible. + +Build 1.4.6-6.5.0.477: + LexManos: + Move chunk compression to the network thread in Packet 51 and 56 + + This will reduce the server load considerably by doing the chunk data compression in writePacketData, which will be run from the network thread. + + The chunk compression can easily use 1/4th of the overall server thread CPU time if someone is exploring much, especially when moving quickly (e.g. with quantum leggings). + + Player, this is how it's properly done. + +Build 1.4.6-6.5.0.476: + LexManos: Fix missed patch in EntityMinecart, #334 + LexManos: Explicitly check if useItem is not denied, allows for denying the item without denying the block + +Build 1.4.6-6.5.0.475: + LexManos: Delete unneeded patch. + +Build 1.4.6-6.5.0.474: + Christian: Some access transformations to allow mystcraft to work again. Closes #331 + +Build 1.4.6-6.5.0.473: + Christian: Fix Fireworks to always work in SMP + +Build 1.4.6-6.5.0.472: + Christian: + Fix up enchantment at the enchanting table vs via a book. Adds in a method + that previously exists under a new name: canApplyAtEnchantingTable() + to determine enchantments that can apply at the enchanting table (a smaller + subset of all possible enchantments for an item, now). Also, add your + enchantments to the anvil book application list, if neccessary. + +Build 1.4.6-6.5.0.471: + Christian: Tweak packet56 to see if this resolves the apparent worldgen derpiness + +Build 1.4.6-6.5.0.470: + Christian: + Server side only item callback: allow a held item to decide if it wants to pass sneak-clicks through + to a block, or not. Defaults false- the same as the new vanilla behaviour (sneak clicks with an item + in hand don't activateBlock anymore). + +Build 1.4.6-6.5.0.469: + Christian: Move another patch up a bit - should actually close #329 + +Build 1.4.6-6.5.0.468: + Christian: Add IPlantable to itemseedfood. Potato and carrot support! + +Build 1.4.6-6.5.0.467: + LexManos: + Update FML: + Fixed issue with users who don't have the JDK installed in there path + MCP will now output bfusicated files with windows reserved names to _name.class and FML will prioritize those names. + Fixed placement of onConnectionClosed callback + LexManos: Updated NetClientHandler for onConnectionClosed placement fix. + LexManos: Fixed cpw's derp in the PlayerInteractEvent logic. + +Build 1.4.6-6.5.0.466: + Christian: Fix ItemInWorldManager so that itemUseFirst works, and the playerinteractevent works. Minor patching mis hit. Apologies. + +Build 1.4.6-6.5.0.465: + Christian: OK. AT the right thing, and add in a call to always get the right thing. Clean up patch fuzz too. + +Build 1.4.6-6.5.0.464: + Christian: Access Transform a couple of methods + +Build 1.4.6-6.5.0.463: + LexManos: Fixe BiomeDecorator AT entry. + +Build 1.4.6-6.5.0.462: + Christian: Drop the item in onBlockHarvested, not breakBlock + +Build 1.4.6-6.5.0.461: + Christian: Fix up block drops for skulls and cocoa + +Build 1.4.6-6.5.0.460: + Christian: And fix up the other equals + +Build 1.4.6-6.5.0.459: + Christian: Fix comparing ItemStack tags for equality + +Build 1.4.6-6.5.0.458: + Christian: + Update FML: 40e57a2 + 40e57a2 Update MCP to newer version Fix fingerprint fire + +Build 1.4.6-6.5.0.457: + Christian: + Fix accidentally removed not-deprecated methods. + + Update FML: d604e44 + d604e44 InterModComms now supports a runtime polling based model for inter-mod comms at runtime. Deprecate method that shouldn't be used. COPY it's content to your mod. Don't CALL it. + 8b7778c Don't be as alarming about item overwrites. + +Build 1.4.6-6.5.0.456: + Christian: Fix binding the texture for multiple render passes. Thanks mdiyo! Closes #320 + +Build 1.4.6-6.5.0.455: + Christian: Allow RenderItem to be easily overridden for things that need to render entityitems. IronChest, BuildCraft, RP2, whatever... + +Build 1.4.6-6.5.0.454: + LexManos: Fixed items dieing improperly due to new EntityItem sync changes, also made items render offset when rendered in 3d. + +Build 1.4.6-6.5.0.453: + Christian: Fix RenderItem so that forge textures load for it + +Build 1.4.6-6.5.0.452: + LexManos: Removed all functions marked as deperacted for the new MC version + LexManos: + Updated FML: + Fixes startclient/startserver + + Added configuration file which allows modids to ignore ID validation checking. IT WILL CRASH YOUR GAME in 99.999% of cases. + + Immibis is a whingy ass. And TinyTimRob too. Now STFU and GTFO. closes fml/#510 for ever. + +Build 1.4.6-6.5.0.451: + LexManos: Inital update to 1.4.6, Version bumped to 6.5 + LexManos: Added jar signing to forge, we sign cpw/* and net/minecraftforge/* + LexManos: Try and print error while signing jar + +Build 1.4.5-6.4.2.448: + Christian: + Update FML: e98c311 + e98c311 Fix up handling null names. *sigh* + +Build 1.4.5-6.4.2.447: + Christian: + Update FML: 293edb3 + 293edb3 Some tweaks to item identification. The GameRegistry methods are deprecated to encourage you to use the new named ones instead. These will force a name on the item/block, allowing for stronger matching t + 9266ff3 Updated MCP download mirriors upon Searge's request. + 31695d5 Fix var name messup + bfb3020 Update released eclipse project to link BouncyCastle + 22a88ea Change ID management slightly. IDs are tracked by block type for itemblock items now. This means servers will need to update. Also, ordinal rearrangements within a mod will no longer trigger server disco + +Build 1.4.5-6.4.2.446: + LexManos: Make PlaySoundAtEntityEvent fire for players as well + LexManos: Remove erronious double call to PlayerDestroyItemEvent + +Build 1.4.5-6.4.2.445: + LexManos: Update FML to fix a typo + +Build 1.4.5-6.4.2.444: + LexManos: Automatically resolved imports. + LexManos: Fixup workspace for BC replacement + LexManos: Manual import fixes + LexManos: + Update FML: + Minecraft is now decompiled into sane package names. + Got rid of the src/common folder as the only folder that exists is src/mincraft, because the client and server codebase is merged. + ID Map generation/validation fixes + LexManos: Updated python scripts for removal of common folder + LexManos: Update patches for repackage. + LexManos: Update AT for repackage + LexManos: Update FML: Fixed the eclipse workspace to reference BouncyCastle library. + +Build 1.4.5-6.4.2.443: + LexManos: Bump version to 6.4.2 to mark the merge of TerrainGen branch. + +Build 1.4.5-6.4.1.442: + Christian: + Added terrain gen events + Added biome color events for @XCompWiz + Christian: Removed unchanged file + Christian: Readded unchanged version + Christian: For @XCompWiz, added a hook to allow mods to dynamically change the color that water is rendered. + Christian: Fix RenderBlocks patch, touch branch + +Build 1.4.5-6.4.1.441: + LexManos: + Update FML: + 2f34290e: Fix possible escape leakage.. + 83deece6: Change ID management slightly. IDs are tracked by block type for ItemBlock items now. This means servers will need to update. + + Also, ordinal rearrangements within a mod will no longer trigger server disconnection, though a warning will still be logged. + LexManos: Derp, imported the repackaged names not old names. + +Build 1.4.5-6.4.1.439: + LexManos: Update readme to reflect the fact that we now download MCP. + +Build 1.4.5-6.4.1.438: + LexManos: Fix ForgeDirection.ROTATION_MATRIX for issue #313 + LexManos: Made GuiContainer.drawSlotInventory overrideable for issue #312 + LexManos: Made BlockButton.func_82535_o overrideable for issue #311 + LexManos: Moved check if map cursor should 'spin' and which world a player respawns in to WorldProvider for PR #308 + LexManos: Add .DS_Store to the gitignore for mac users + LexManos: Add CloudRenderer for PR #304 + LexManos: New TileEntity function to determine if the TE should be destroied when Block/Meta changes, For Issue #300 + LexManos: Pistons now respect world height a little better, for PR #286, will review Rotation event later. + LexManos: Creative GUI will now remember what tab page you were on, thanks Matchlighter PR #290 + LexManos: Fix type in ForgeChunkManager.ticketCountAvaILableFor, thanks iChun PR: #285 + LexManos: Fixes crash when mods add TreeMaps to the Configuration map, thanks AtomicStryker, PR: #282 + LexManos: Fix shouldRiderSit() to properly determine if rider is sitting. + LexManos: Expose BiomeDecorator fields for custom Biome information, Issue: #239 + LexManos: Make BlockButton.sensible protected and non-final + +Build 1.4.5-6.4.1.437: + LexManos: Fix for FML Fixes eclipse workspace, now sets execution bits on astyle-osx, and mcp .sh files. Also now kills install if astyle is not found. + +Build 1.4.5-6.4.1.436: + LexManos: Fixed release -skipchangelog + LexManos: Fixed install --mcp-dir + LexManos: + Updated FML to 486: + 02b54ca8: Add a modid on the mods list, should make it easier to do things like forge chunkloading config + 70670f2f: Fix build eclipse extractor task + b9fa3fc9: Add in storage, detection and validation of the ItemID array between client and server + 5e5d8206: Fixing id map generation - should validate correctly now + f9fc06a8: Remade python dist as 32-bit. + 93d47a1c: Reenabled replacement of the eclipse folder. + +Build 1.4.5-6.4.1.435: + LexManos: And finally, fix up the release install script to have a parameter to specity the MCP directory. + +Build 1.4.5-6.4.1.434: + LexManos: Fix Forge install script to use python shipped with FML. + +Build 1.4.5-6.4.1.433: + LexManos: Push didn't get all of eclipse.... + +Build 1.4.5-6.4.1.432: + LexManos: + Cleaned up eclipse workspace and moved it to a zip file. + Setup will extract it if it doesn't already exist. + +Build 430: + Christian: Fix release script for new forge job name. Add in skip changelog option for local builds + +Build 1.4.5-6.4.1.426: + LexManos: Remove MCP from released src distro.. whops. + +Build 1.4.5-6.4.1.425: + Christian: + Fix static method, closes #271 + Don't spam the log if a mod requests a ticket beyond their maximum. A single message is fine. + +Build 1.4.5-6.4.1.424: + GuntherDW: Fix typo in chunkmanager config + Christian: fix List configs + Christian: Add Chunk Watch and UnWatch events. + +Build 1.4.5-6.4.1.414: + Christian: + Tweak release.py script: HEAD==master in general + + Update FML: 26a2ef5 + 26a2ef5 Add a modid on the mods list, should make it easier to do things like fo + b4e3490 Fix build eclipse extractor task + 828341f Fix typo + 7a8dae5 Try and make branch non-derpy + d3c0e1f Try defaulting branch differently + 9c77d3f Remove old eclipse workspace and ship/extract as a zip file (use ant set + 6371e9b Assume Forge is the parent directory when checking for AT configs. + 516954e Name both zips the same format + 1151d5c Last part of branch name only please + 25f1dda Try and get a valid branch name on jenkins + 39a146f Clean up build.xml script- support outputting a branched jar file + 037dcae Small derp in install.py + 01d6da3 Try marking python as binary. + 8b26659 FML will now attempt to download MCP as part of the install process Now + 42b3e6a Windows Python distrabution created using py2exe, thanks Fesh0r for the + e709ab8 Marked more spots in gui/items bitmask as being free. + +Build 1.4.5-6.4.1.413: + LexManos: Add wget and unzip from GnuWin32 for use in the setup scripts. + LexManos: Update FML, now includes python, and will download MCP for you + LexManos: Update python files to add support for specifying MCP directory and FML downloading MCP on the fly. + LexManos: Fix issue with DimensionManager.shouldLoadSpawn + +Build 1.4.5-6.4.1.411: + Christian: Fix #289 + +Build 1.4.5-6.4.1.410: + Christian: + Add in some additional ticket loading callbacks for ChickenBones. Closes #284 + Add in a call to determine if there are possible chunktickets for a world. This should let + Mystcraft and other dynamic world generating mods check if they should immediately load + a world based on existing chunk tickets. + +Build 1.4.5-6.4.1.409: + LexManos: Fix issue where non-BlockContainer TEs would not be removed properly. + +Build 1.4.5-6.4.1.408: + LexManos: Fix logic issue in ForgeChunkManager.ticketCountAvaliableFor(username) PR + LexManos: Fix issue where worlds were potentially unloaded improperly on server shutdown. + +Build 1.4.5-6.4.1.407: + LexManos: Small change to fix loading configs. + +Build 1.4.5-6.4.1.406: + LexManos: Added section of the EntityData NBT that will be persisted for players across respawning. + +Build 1.4.5-6.4.1.405: + LexManos: Exposed some ChunkLoader information for PR #278 + LexManos: Fix potential NPE in EntityJoin handler and print warning. + +Build 1.4.5-6.4.1.404: + LexManos: Small tweak to config to use Treemap + +Build 1.4.5-6.4.1.403: + xcompwiz: + Adds a more intelligent chunk constructor + + Adds a chunk constructor with full block id range, that's metadata + sensitive, has intelligent coord ordering, and which allows for + generation at greater heights than 127. + xcompwiz: Fixes some weird formatting + zach: + Fixes ordering for z & x loops (proper array increment order) + + The ordering before would skip about in the ids and metadata arrays. This runs linearly and should improve performance. + Done live on github + +Build 1.4.5-6.4.1.402: + LexManos: Fixed AIOOB issue with new sanity check in getBlock + +Build 1.4.5-6.4.1.401: + LexManos: Fix potential NPE in saving a property that didn't define a type. + +Build 1.4.5-6.4.1.400: + LexManos: + Added new functions to the Config class to allow for specification of comments when getting properties. + Added new getTerrainBlock whihc will limit the resuling ID to < 256, useful for world gen. + Calls to getBlock with IDs less then 256 will be assigned values above 256 {Thus freeing up terrain gen slots} + Made ConfigCategory implement Map should fix compatibility with most mods that broke two builds ago. + +Build 1.4.5-6.4.0.399: + xhamolk.class11: + OreDictionary addition: getOreID(ItemStack) + + Allow getting the oreID from a ItemStack, as an alternative from getOreID(String). + + Now is easier to exchange items for their equivalencies through the OreDictionary. + +Build 1.4.5-6.4.0.398: + LexManos: New nested configuration category support, for PR #258 + +Build 1.4.5-6.4.0.397: + LexManos: Fixed issue with abandoned chunks. + LexManos: Fixed random chest items not generating to there max stack sizes. + +Build 1.4.5-6.4.0.396: + ohai.iChun: + Add check that Entity saves to disk before saving entity to saved chunkloading data. + + Returning false to addEntityID prevents the entity from being saved (cred to LexManos) + Entities which do not save to disk but are chunkloaders causes errors the next time the world loads. This ought to fix it. + +Build 1.4.5-6.4.0.395: + LexManos: Updated FML, proper free-sprite list for /gui/items.png, and fixed MCP version. + +Build 1.4.5-6.4.0.394: + Christian: + Move server specific patch back to common from minecraft. + + Update FML: 1f5c58b + 1f5c58b Fix up MCP version + 61e4db2 Merge joined.exc add update howto + 5efc1eb Revert "Fix MC-2497 - derpy torch flames. Moved from forge. Everyone sho + 3b3600d Modified merger to just consider all server classes as common. And moved + edcc5ca Forgot mcp.cfg + ca79dfe Updated to 1.4.5b + 5945279 Fix python error + 57bf643 Modified decompile so that we do not have to decompile the server, saves + d9d0a46 Cleaned up some tabs + 2fac644 Change usage of ZipFile, fixes issues with Mac's default instation of py + +Build 1.4.5-6.4.0.393: + LexManos: Fix TE Issue + +Build 1.4.5-6.4.0.390: + LexManos: Removed GnuWin32 programs, should no longer be needed as we've moved to python. + LexManos: Updated to MC 1.4.5b + +Build 1.4.5-6.4.0.388: + Christian: Stupid eclipse resetting preferences. Spaces, not tabs!!! + Christian: + Update FML: 570592b + 570592b Attempt to fix a possible comodification risk + +Build 1.4.5-6.4.0.387: + Christian: Add in events when forcing and unforcing chunks. Hope this works for you ChickenBones! + +Build 1.4.5-6.4.0.386: + Christian: + Update FML: fde9414 + fde9414 Change texturebinding to use the textureId directly rather than iconInde + +Build 1.4.5-6.4.0.385: + LexManos: Fixed issue with default implementation of Beach plant type check + LexManos: Fixed custom soil checks for NetherStalk and Reeds + +Build 1.4.5-6.4.0.384: + LexManos: Release script will now include the Minecraft version in the arcive names. + +Build 6.4.0.383: + Christian: + Update FML: f348496 + f348496 Fix mod display list. Shouldn't be derpy anymore. + a5c31b5 Fix World patch- workaround no longer required and leftovers are bad. + 2dc3f0d Cleaned up the AT config updater, should work for any setup of the MCP w + +Build 6.4.0.382: + LexManos: Fixed beds not working properly when respawning. + +Build 6.4.0.381: + Christian: + Remove EntityFX patch - moved to FML. Patch SuspiciousClasses to add "forge" + + Update FML: fa56701 + fa56701 Fix MC-2497 - derpy torch flames. Moved from forge. Everyone should benefit. + 96935bb Fix an NPE in TileEntity trying to generate a crash report. Make suspicious classes just return the obvious. Fix NPE for FML crash report on server. Clean up dead pa + +Build 6.4.0.380: + Christian: Fix the derpy torch flames properly. Closes MC-2497 properly ;) + +Build 6.4.0.379: + Christian: + Update to MC 1.4.5 + Update FML: 43d3042 + 43d3042 Clean up a patch + fec221f Update FML for MC 1.4.5 + b0f0635 Fix for ModLoader static overrides not displaying. + 23a2513 Fix up derpy file name handling: closes #127 Fix up duping childmods: cl + a6eaa2b Fix spelling issue. + c6a0741 FML setup should now download and verify all the minecraft files needed + 5a1930e Small fix to some MCP metadata for pre2 + 418deba Update to MC 1.4.4 + 4ff2cff Fix bug when a modloader mod is run on a dedicated server and has a conn + +Build 6.3.0.378: + Christian: + Some liquid events. Non-API breaking. Add them at your leisure. This helps liquids know + what's happening to them. In case they're volatile or something ;) + +Build 6.3.0.377: + Christian: Another difference + +Build 6.3.0.376: + Christian: + Fix fillLiquidContainer - return null, not the empty container for + previous API compatibility + +Build 6.3.0.375: + LexManos: Update FML to fix ModLoader/addOverride functionality. + +Build 6.3.0.374: + kinglemming: + Fixed oversights in liquid registry, added functionality for instant checks as requested by CJ. + Liquid list return is now secure. + + Signed-off-by: King Lemming + Christian: Change name to LiquidContainerRegistry. Fix up missing API. This better be it now! + +Build 6.3.0.373: + kinglemming: + Liquid Manager Rewrite - slightly more memory usage, loads more efficient at runtime. + Added default Lava and Water registrations to Liquid Manager. + Removed method which relied on flawed assumption that a given liquid only had a single type of container. + + Signed-off-by: King Lemming + Christian: Rename some liquid stuff for more sensible naming. Tweak API slightly. + +Build 6.3.0.372: + Christian: + Add in the basic IBlockLiquid interface. The actual block implementation + will follow. + +Build 6.3.0.371: + Christian: + Add an ItemStack sensitive version of getIconForRenderPass, defers to + existing by default. Should allow NBT data to affect multipass icon rendering. + +Build 6.3.0.370: + Christian: Change getLeft to getRotation, around any axis. + +Build 6.3.0.369: + Christian: Some new stuff on the liquid API for better RP2 interaction. Coming soon: blocks! + +Build 6.3.0.367: + Christian: Tweak liquidcontainer API for sidedness capability + +Build 6.3.0.366: + LexManos: Updated FML, Fixed spelling issues in logs, and file name issues. + LexManos: Added saplings and leaves to the ore dict. PR: #242 + LexManos: Mods can use custom textures for Slot background overlay, instead of only items.png PR #245 and #246 + LexManos: Fix issue where players would spawn in the ground. + LexManos: Allow for modders to decide whether or not players are positioned on ridingEntities similarly to pigs PR #244 + LexManos: Fix breaking despite cancel in creative mode for PlayerInteractEvent OR #247 + +Build 6.3.0.364: + Christian: + As agreed, liquid API from BuildCraft has been migrated to minecraft forge. There will be cleanups to follow, + including sided handling and client rendering tweaks. Stay tuned! + Tweak forgedirection for a couple of naming constants + +Build 6.3.0.363: + Christian: Add toLeft rotational data to ForgeDirection + +Build 6.3.0.362: + LexManos: New field in ForgeDirection to hold just the valid directions. And made getOrientation a bit more efficient. + +Build 6.3.0.361: + LexManos: Fix entity items rendering on the ground. + LexManos: Fix crops not droping seeds properly. + +Build 6.3.0.360: + LexManos: Update to 1.4.4 + LexManos: Bumped version number for 1.4.4 + +Build 6.2.1.358: + LexManos: Ore Dictionary Improvements, replaces vanilla recipes with OreDict recipes for some vanilla items. Thanks Covert. + +Build 6.2.1.357: + LexManos: Made Item constructor public, and made setTextureFile chainable, requires mods to recompile, but as this is for 1.4.3 i'm not concerned. + +Build 6.2.1.356: + Christian: + Temporary update for 1.4.3 + Update FML: bd2123c + bd2123c Clean up some patch fuzz + c2a603c Update joined.exc file from latest conf + db12af4 Update to 1.4.3 + LexManos: + Readdaed teleporter sensitive versions of the teleport function + Added per-world list of custom teleporters, Modders, use World.Load to populate this list. + Changed the definition of our version schemes. + +Build 6.0.1.355: + Christian: + Update FML: 8356fe9 + 8356fe9 FUUUUUU! Stupid ticking. STOP REGISTERING TICK HANDLERS IN YOUR CONSTRUC + 6edce8b Patch a file handle leak in RegionFileCache handling when under memory p + ca2bbe0 Update MCP mapings again, yay bad syncs. + c8941a7 Updated MCP mapings. + 5e20c03 StartServer is now useable in merged code base. + +Build 6.0.1.354: + LexManos: Update FML: Updated MCP mapings, and fixed StartServer + +Build 6.0.1.353: + Christian: + Update FML: b19e882 + b19e882 Remove some debug, closes #123 + 9d7d32a Fix up tick management outside of mod loading phases for ModLoader mods. + d512539 Negatively cache failed class lookups, should help with @SideOnly performance issues. + +Build 6.0.1.351: + Christian: + This update fixes some world corrupting vanilla error handling, mostly caused by + mods doing something derpy. Hopefully, your world saves will thank me. + Note that if the mod does derp, it's data is lost. It's not FML or Minecraft Forge's + responsibility if "DirtChest 2000 Mk5" can't write their TileEntity method correctly + and you lose your 100000 diamonds. + + Update FML: a3a93f3 + a3a93f3 Fix some whitespace issues, handle possibly bugged entities as well + a7eb5dc Vanilla/mod bug fixes: CME on entity processing, TileEntity resetting chunk on save/load. + +Build 6.0.1.350: + LexManos: Fix cast issue when mods attempt to make fake worlds. + LexManos: Fix a vanilla bug related to certian seeds and stronholds. + LexManos: Cleanup some debug in the script, and fix version number for the current build. + +Build 6.0.1.349: + LexManos: Change logs should now be bundled with downloads, as well as avalible on files.minecraftforge.net + +Build 6.0.1.348: + Christian: + Update FML:b23081d + b23081d Support for ModLoader 1.4.2. Good job Risu! + 66db4ec Add in an exclusion list tag for @Mod. The backend code isn't yet implemented, but shows the basic idea. + +Build 6.0.1.345: + LexManos: EntityLiving.experianceValue private->public for Issue #225 + LexManos: Changeable name tag render distance for PR: 174 + LexManos: Remove some debug code + LexManos: New hook to allow items to be on multiple creative tabs PR 176 + LexManos: Fix issue where scroll bar would not render when switching tab pages. + LexManos: Added metadata sensitve experience to furnace recipies and added Item callback to determine experience gain. + LexManos: Exposed functions for adding superflat presets. + LexManos: New hook to allow for custom beacon support blocks. + CovertJaguar: + Added vanilla wood + dye to the Ore Dict + + Should help simplify interaction between mods that add similar items. + LexManos: Added Stonghold, Village, and Spawn biome management helpers for PR 207 + LexManos: Fix compile issue. + +Build 6.0.1.343: + csendek: Add event to cancel or change chat messages sent from server + csendek: Add event to cancel or change chat messages sent from server + +Build 6.0.1.342: + LexManos: Pickblock will new compare NBT data, should allow mods to refine there result better. + LexManos: Added system to place all configs that use Forge's Configuration function into a single file on disc. Optional config to enable this. Blame MattaBase for this idea.. + +Build 6.0.1.341: + LexManos: Fix ItemSeed placement to take into account the IPlantable interface. + +Build 6.0.1.339: + LexManos: Added a per-world MapStorage feature that provides a method to store data files associated with a specific world, as well as fixes the issue with villagers loosing there village. {Where villagers wouldn't go inside at night} + +Build 6.0.1.338: + pahimar: Update patches/common/net/minecraft/src/SlotCrafting.java.patch + Christian: + Fix the accessor and type on World.getPersistentChunksFor() + Update FML: 8bd98c3 + 8bd98c3 Fix a small problem with the new MCP container- it can cause NPEs. Nice. + 34cc42d Fix up some javadoc complaints + +Build 6.0.1.337: + Christian: + MCP information is now included in Minecraft Forge. They deserve the credit :) + + Update FML: 09eade4 + 09eade4 Update build - include MCP information in the source pack + 9bfe7df MCP deserves lots of credit. So here it is. + +Build 6.0.1.332: + Christian: + Update FML: 8006b77 + 8006b77 Fix instantiation bug + 7cc91cf Simple InterMod comms. Send a message using FMLInterModComms.sendMessage(). Receive messages through an @IMCCallback + 24d7285 Update for MC 1.4.2 + cedf3d5 Duplicate Mod display screen. + +Build 6.0.1.331: + LexManos: Downgrade to FML 415, issue with new IMC system. + +Build 6.0.1.330: + LexManos: Update to 1.4.2, obf stayed the same, so only small revision. + +Build 6.0.0.329: + LexManos: Update World patch to fix map provider issue. + +Build 6.0.0.328: + Christian: + Add an access transform for Block.setBlockBounds - should help a lot of code that uses this. + + Update FML: d915f39 + d915f39 Fix typo in access transformer + 346691c Fix the merge for forge + 6dadc1d Fix up references to minecraftDir in obf code + 8a55f68 Update FML for Minecraft 1.4.1 + 5645fa5 First update to 1.4.1, patches need updating. + ff0f00f Add some null checks, and throw descriptive exception when SideTransformer prevents a class from loading. + +Build 6.0.0.327: + LexManos: Update to MC 1.4.1 and Forge 6.0.0 + +Build 5.0.0.326: + LexManos: Fix color issues with Dyed Leather armor. + +Build 5.0.0.325: + LexManos: Remove deprecated code. + LexManos: Cleanup mirrored things in ShapedOreRecipe to address issue 208 and issue 210 + LexManos: Fix up the setHandeled/setHandled for events, it is now a generalized setResult, the meaning of which is defined by each event. + +Build 5.0.0.324: + LexManos: Fix NPE issue when mods add null EntityFX's to be rendererd. + +Build 5.0.0.323: + Christian: + Update FML: 7a34246 + 7a34246 Ship a client only class so the merge works both sides. + +Build 5.0.0.322: + Christian: Fix install.py so it can be installed properly from src distribution + +Build 5.0.0.321: + Christian: + Update FML (entire change log since last by me): 62a6b52 + 62a6b52 Fix RenderRegistry render ID - now at 36 Fix SpriteMaps - they're running out fast! Switch to forge if you can! RenderBiped now has an armo + b845cde Fix up FML python script for forge + 2c36dfb Update to MCP pre 3. Fixes some weirdnesses with explosions. + acd880d Merge branch '1.4' + e6f57e5 Update for MCP release + b1de0fc Remove fuzz from patches for INetworkManager change + ad44619 Fix for new names INetworkManager and ITexturePack. I also provide refactoring scripts for both, for your use... + 3a94211 Add a script to ignore git complaining about the eclipse workspace changing - run when you first open the eclipse workspace + 9386e23 Update gitignore - ignore the files that will change or be deleted + 10b318c Add in "starter" image for eclipse workspace + 995d5b7 Eclipse workspace fixup 2? + bbb12c8 Clean up eclipse workspace- part 1 + b50058e Clean up a bit of patch fuzz + 4a0bfa9 Fix CommonHandler for rename of the server thread class + 4d1ec66 Update with RC2 MCP. Use the joined.exc file directly since MCP now provides it. + b52e3d0 Fix exception for CodecMus + a1011c9 Fix some compilation issues. Requires resolving the worldclient abstract method + 0ebd4f3 Inital patch update to 1.4 + 89c68c4 Moved into EntityVillager + e4702cb Fix line ending detection during checkout. + 6e64fd7 We don't need logging here... + 014b3f4 remove useless -Server projects + 7628c67 Updated the AT config and Marker config for 1.4 obf + 54a322c Updae build to copy files that may decompile differently but are actually the same, and updated for 1.4 values. + aadf396 Update MCP Merge config. + 81e1855 Pull in MCP's 1.4 conf files. + 5686fd4 Fix error in merging of exec's + +Build 5.0.0.320: + LexManos: FML Update for 1.4 + LexManos: Fix recompile call for 1.4 MCP change + LexManos: Update Forge's AT for 1.4 names. + LexManos: Update for new names in 1.4 + LexManos: Remove final from 'villageCollectionObj' to allow for delayed setup of Map source objects. + LexManos: Moved to common where it should be + LexManos: No longer needed, getRenderDistance removed because Vanilla has it now, TileEntity.func_82115_m + LexManos: Forge spawn protection removed, now a vanilla feature. + LexManos: Patches that applied with little to no functional changes. + LexManos: Updated Render patches, Bipeds now have the ability to have helmets, of either blocks, or the new 'heads' + LexManos: BlockSnow now pretty much just passes harvestBlock to it's super. + LexManos: Entities have a new function to determine explosion strength that they create, and implemented Item frame pickblock. + LexManos: Patches removing forge spawn protection, now a vanilla feature. + LexManos: BlockCrops is now the parent class of multiple types of crops, Wheat, Potatoes, and Carrots, updated getBlockDropped patch to reflect. + LexManos: Removed Forge's TileEntity.getRenderDistance, now a vanilla feature: TileEntity.func_82115_m + LexManos: Teleporting has a new function (func_82448_a), made it so that you can supply your own teleporter to that function, and so it works with dimension movement factors. + LexManos: Updated World patch, there are a lot of new things happening int he constructor, xcomp should probably vet this. + LexManos: Updated Forge version to 5.0.0, for 1.4 + LexManos: Update release script for mcp changes, releases now build. + +Build 4.3.5.318: + LexManos: Remove some test code. + LexManos: Bump version to 4.3, final for 1.3.2 + +Build 4.2.5.317: + LexManos: Added ability for mods to cleanly define and display new creative tabs. + +Build 4.2.5.316: + Christian: Fix classloading issue preventing JRockit from running forge + +Build 4.2.5.315: + Christian: + Forge build change: the universal jar, if installed alongside a server jar file, can be executed to load forge. No server side merging required! + Update FML: 989ab3e + 989ab3e Update build.xml - add a classpath to the manifest. This means the universal jar is now a server side launche + 88f1dc9 Typos! + +Build 4.2.5.314: + Christian: + Update FML: 470a185 + 470a185 Try and avoid an NPE in crash reports + 0029518 Fix logging problems- log death messages and re-route the server through FML logs properly. + fe7a832 Fix the test mod + a2bc30b Coremods can be specified via the command line: fml.coreMod.load= This will help with developing coremods + bae1f74 Fix classpath for server + 887aa27 Add in the ability for a client to throw a custom exception that displays a custom gui instead of the default error message. + e17f267 Update FML internal classpath. Helpful for coremod devs + 3b9972a Don't use File to separate the last element of the path. It's a URL, they're always separated by '/' + 5c96afe Changed RelauchLibraryManager so that files with different paths on the same baseURL can be implemented in one ILibrarySet. + 1bca393 FML as a whole is now subject to transformers. + 27cf731 Bonus extra character. thanks randomitter! + 94c84cd Merge pull request #116 from Vazkii/patch-1 + c2d3195 Add Null check to FMLClientHandler.sendPacket + +Build 4.2.5.313: + LexManos: Make Item.createEntity only call when its specifically a EntityItem, not a subclass of it. + +Build 4.2.5.312: + LexManos: New Block hook to determine if it can be destroied by the ender dragon, for PR 199 + LexManos: Ship forgeversion.properties with release zips. Same format as fmlversion.properties. + LexManos: Dll files are binary as well. + +Build 4.2.5.311: + LexManos: Fill out the .gitattributes to help with line endings how they should be. + LexManos: Change the Crafting damage check to be the same as the usage damage check. (>= -> >) + LexManos: Allow hook into GuiSlot for background rendering for PR #203 + +Build 4.2.5.310: + Christian: Reorganize the forced chunks a bit- offload the cost of immutable map building to the mods, rather than the chunk tick + +Build 4.2.5.307: + Christian: Remove @SideOnly from removePotionEffect. Hi RichardG! + +Build 4.2.5.306: + LexManos: Add some accessors to teh ChunkLoader tickets. + +Build 4.2.5.305: + ohai.iChun: Fix not passing right render pass to Item class. + +Build 4.2.5.303: + Christian: Fix bounds checking on chunkcache. Should fix a bunch of rp2 and maybe other extended tile entity code + +Build 4.2.5.302: + Christian: + Use weak references to the worlds in our maps. WorldClient would otherwise + leak all over the show. + +Build 4.2.5.299: + mehvids: Add some of the model subsystem to the server. The part that doesn't require openGL. This allows for systems that dual models as collision/selection boxes etc. + xcompwiz: + Improves DimensionManager + + Adds handling for unloading and hotloading of worlds, fixes some typos, + allows for dimensions to be unregistered (allowing save specific + dimension registrations), general changes to match these features. + xcompwiz: + Adds world unloading and hotloading calls + + Adds world unloaded message to MinecraftServer on save. + Adds world unloading calls to chunk provider/manager when all chunks are + unloaded. + Adds call in MinecraftServer getWorld to hotload world if it isn't + loaded. + xcompwiz: + MapStorage Fix + + Fixes setting and timing of map storage object to allow for the + WorldProvider to use it during initialization + Forces single instance of map storage object (per side) + Moves setting of spawn to after provider setup + xcompwiz: + Lighting Time fix + + score_under's lighting fix that limits and fairly distributes the amount + of time spent on recalculating lighting + xcompwiz: + Server player concurrency fix + + Fixes an issue where the server can move a player while the player is + moving, process the player's last move (putting the player back where + they were before the teleport), and then complain about the player + moving too fast when the client catches up to it's new position. Also + fixes this issue while riding an entity. Only affects player + client/server movement sync. + Christian: + Remove dead WorldInfo patch. Tweak dimension code a bit for better naming, and use the new FML world loading facilities + Update FML: d0e7c9e + d0e7c9e Update patches *sigh* + f3e1cac Add in a savehandler strategy for reading and writing data to the world save. This service is only available to coremods via the WorldAccessConta + 51fb513 Add in some bukkit supporting code. Most of this is unimplemented until the bukkit coremod is complete. + 65c9fdd New stuff on the ModLoader! Risu has been busy. Closes #114 + c1d4458 Mods can now declare a range of minecraft versions they will run against + Christian: Update forge to 4.2 + +Build 4.1.4.298: + LexManos: Fix issue where light would not properly recalculate. + LexManos: Fix issue where mushrooms would not check the proper soil block. + +Build 4.1.4.297: + LexManos: Expanded DungeonHooks to allow for adding of custom DungeonLoot values directly, allowing for better control over the generated items. + +Build 4.1.4.296: + LexManos: Made PlayerEvent.BreakSpeed fire when a player cannot harvest the block. Issues #191 and #188 + +Build 4.1.4.295: + Christian: Dormant chunk cache might actually work now, and not mix chunks across worlds + Christian: + Update FML: ca1ca4f + ca1ca4f Fix maps supporting greater than byte() dimension sizing + 15ee8bf Fix language registry additions, closes #113 + a08b5b1 Merge pull request #112 from pahimar/master + 8dac58f Added ability to query the Language Registry by key and language for specific localized text, as well as loading in localization text f + +Build 4.1.4.294: + LexManos: Fix buckets, need to rethink for bukkit compatibility. + LexManos: Fix vanilla bug where the player would load chunks outside its range that would be 'abandoned' and never unloaded. + LexManos: Forgot comment, you see nothing... + +Build 4.1.4.292: + Christian: + A few requested features of ForgeChunkManager. Mods can specify a + chunkloading config directly in the config file, including chunk + loading overrides if they wish (and the config allows them). + Also added "player" tied tickets that bind to the player and not the + mod's quota. + +Build 4.1.4.291: + LexManos: Missing updates from last commit + LexManos: Location aware version og Block.lightOpacity for PR #169 + +Build 4.1.4.290: + LexManos: Fix bug in last commit that caused block to not break. + LexManos: Added preliminarty Player Interact event heavily based on the bukkit event. + +Build 4.1.4.289: + LexManos: Add PlayerEvent.HarvestCheck and PlayerEvent.BreakSpeed for dealing with things related to a player harvesting a block. + +Build 4.1.4.288: + LexManos: Fix bug where breaking texture would not apply to top/bottom of beds. + +Build 4.1.4.287: + Christian: Add in an "EntityEvent.EnteringChunk" event. Useful for your entity chunkloading stuff. + +Build 4.1.4.286: + LexManos: Support for no BOM. + +Build 4.1.4.285: + Christian: + Update FML: f083707 + f083707 Extreme headless mode is back! + +Build 4.1.4.284: + Christian: + Update FML: dd39ae5 + dd7502a Fix parent child counts showing properly. Closes #107 thanks scott! + b36d447 It's useMetadata, not usesMetadata. thanks myrathi and psx. closes #110 + efb1066 Fix random shuffling when manipulating biomes by using a LinkedHashSet to preserve iteration order. Closes #111 + Christian: + Some more tweaks to the chunkloading code. The world.load event fires slightly later- once the entity watcher is + set up, so entities can actually load into the server world. Also, tickets actually save and load properly + and null entities don't break the server + +Build 4.1.4.282: + LexManos: > != >= + +Build 4.1.4.281: + LexManos: Fix Configuration.getItem to return pre-shifted values suitible to pass into Item constructors. + +Build 4.1.4.280: + LexManos: Fire off PlayerDestroyItemEvent for crafting contianer items that get damaged to much. PR #183 + +Build 4.1.4.279: + LexManos: Configuration will now attempt to detect the encoding of the file using the Byte Order Mark. + LexManos: + Changed Configuration to use overloaded methods, and changed the order of arguments to be Category, Key, Value instead of Key, Category, Value to hopefully help cleanup some peopels code. + Added function to get a free Item id. Will only accept values that are not in the block space. Needs testing. + Marked all the old getOrCreate* functions as deprecated. + +Build 4.1.4.278: + Christian: Fix some errors if the config is unparseable. It should log an exception and carry on with defaults. + Christian: Support quoting in property and category names for almost all possible characters allowed + Christian: Fix up some config file handling to be even more resilient. + +Build 4.1.4.277: + Christian: + More modifications to the ticket callback handling system. There are up to two callbacks during world loading now. One to allow + selective preference for ticket types. The other to actually allow the mod to force chunks. + +Build 4.1.4.276: + Christian: + Update FML: dd39ae5 + dd39ae5 Try and fix the newline capture so consoles show on the server on windows again + b39f808 Readd ancient searge name so dan200 and computercraft can run again. REMOVE THAT CODE DAN. + Christian: + Some changes to the ForgeChunkManager. + Null modData is allowed now. + The entity id is actually properly persisted in the ticket now. + There is a new "orderedLoadingCallback" that allows you to provide a preferred ticket loading order in case of "excess tickets". Also, tickets + that are not in the returned list are now unregistered. + There is a way to resort the chunks in the forced chunklist on a ticket now. + Log the dormant cache configuration + +Build 4.1.4.275: + Christian: Simple chunkloading implementation + Christian: Working cross dimensional implementation of chunkloading for Forge. + Christian: + A lot of tweaks to the chunkloading for entity behaviour. Entities are now bound by a new + persistent id they *all* have, on the server side. + Christian: Remove @SideOnly flag for function now required on the server + Christian: Fix NPE in ForgeChunkManager + Christian: And a concurrentmodificationexception in the same code + Christian: + Simple chunk caching capability for the chunkloader. This will store "dormant" chunks in a + configurable cache, potentially saving the cost of reloading them from disk. + Christian: Delete some debug code + Christian: Some more fixes for chunkloading code. Works very reliably now. + Christian: + Update FML: a2c059e + a2c059e Fix missing import. Thanks ichun :( + 19316a0 Version file searching should work for directories too + fbc7a5c Fix bug causing NPE if non-whitelisted player joins an FML server without FML installed + e9cfd10 Merge pull request #103 from iChun/patch-2 + 1424883 Fixed ModTextureAnimation not updating and not binding to correct image. + +Build 4.1.4.274: + mitchpetrie29: + Update patches/common/net/minecraft/src/WorldProvider.java.patch + + Fixed WorldProvider.setDimension() setting the wrong variable. + LexManos: Fix patch errors in merge of last PR -.- + +Build 4.1.4.272: + LexManos: Fix accedental doubling of shift, Fixes saplings/flowers planting a space above where they should + +Build 4.1.4.271: + LexManos: New Plant API that allows for custom plants/soils. + +Build 4.1.3.270: + LexManos: Added new hooks for modifying the items generated in chests during world gen. + +Build 4.1.2.269: + LexManos: Redirect a lot of functions through WorldProvider for Mystcraft, allowing them to be overriden by the provider. + +Build 4.1.2.268: + CovertJaguar: Fixed Entity Item render helper + +Build 4.1.2.267: + ohai.iChun: Fix pick block key giving invalid spawn eggs + ohai.iChun: Logic derped + ohai.iChun: Logic derp (again) + +Build 4.1.2.266: + LexManos: Added side sensitivity to standard EntityDiggingFX, added Block functions to override spawning of digging and breaking effects. + +Build 4.1.2.265: + LexManos: Fix potential NPE in Custom item entity span code caused by item id misconfigurations. + +Build 4.1.2.264: + LexManos: Fix recursion issue with new special item entity code. + +Build 4.1.2.263: + Christian: + Update FML: 6c746ec + 6c746ec Tidy code + aacbfeb Change version.properties search slightly, allow access to found file from the preinit event + 1513b36 Add in the capability to read an internal version as .version from a file in the zip call versi + 35852a6 Fix up reversed assignment for version fallback + +Build 4.1.2.261: + pahimar: Changes to how PotionEffects are removed, now each PotionEffect can specify which ItemStacks can remove it's effect + pahimar: Inverted logic derp + +Build 4.1.2.260: + LexManos: Package all of paulscode/ with the universal jar to help mac users who cant understand how to merge folders -.- + +Build 4.1.2.259: + LexManos: Bump version to 4.1.2 to mark the end of todays changes. + +Build 4.1.1.258: + LexManos: Fix potential issue with custom events where the constructor is not visible from the EventBus class, Should never arise but meh. + LexManos: Read/Write config files as UTF-8 instead of ANSII, should fix issue with Turkish people. + LexManos: Allow for respawning in different dimensions. + LexManos: Move spawn fuzz to WorldProvider/WorldType, useful for VoidWorld types. + +Build 4.1.1.257: + LexManos: Make EntityJoinWorldEvent Cancelable properly + LexManos: Move Forge init message to MinecraftForge.initalize + LexManos: Add ability for Items to create custom Entities for themselves when added to the world, PR: #151 + +Build 4.1.1.256: + admin: Allow flight,player sensitive + +Build 4.1.1.255: + LexManos: Fix a few patch fuzzes + LexManos: Implemented isBlockFoliage for pull #141 + +Build 4.1.1.254: + LexManos: Clean up some javadoc warnings. + LexManos: + New EntityItem related events + Fixed player death event in SMP + Added Player specific drops event + Added generic EntityJoinWorldEvent + +Build 4.1.1.253: + LexManos: Fix FillBucketEvent to work with stacked buckets. New change in 1.3 + LexManos: EntityLiving variation of drawBlockDamageTexture to allow for Non-player viewports. + +Build 4.1.1.252: + Christian: Remove all references to ModLoader + Christian: + Update FML: 89b8236 + 89b8236 Fix onConnectionClosed not being called on the client side of a connection + +Build 4.1.1.251: + LexManos: Bump version to 4.1.1 + +Build 4.0.0.250: + Christian: + Update FML: 290a3c9 + 290a3c9 Merge pull request #94 from iChun/patch-1 + 9485dc5 Fix handleTinyPacket using short instead of int. + Christian: + Update FML: 70f55c5 + 70f55c5 Add in exclusion list for IFMLLoadingPlugin. + 1c1716d Remove debug cruft + d567f79 To properly align with packet9respawn, make packet1login's dimension an int as well + ecd4e46 Attempt to build some protocol negotiation, and fix packet1login so that the dimension is a short, matching packet9respawn + 16fe495 Compatibility level is now passed from server to client - and the client tracks it. This means certain packet changes can be made without b + 4f70d23 Fix dependency checking: you can depend on specific versions of FML + +Build 4.0.0.249: + Christian: + Update FML: e6abb69 + e6abb69 Fix up tiny packet handler to actually work + e862052 Allow javax to be shipped by mods. Should allow some more modloader mods to work unaltered + 4071ad1 Add in a little bit of protocol cleanup. Start work on allowing multiple protocol versions + 6061964 Give a way to build the packet131mapdata packet correctly populated + b818769 Capture and leverage Packet131MapData + +Build 4.0.0.248: + LexManos: Fix placing torches and the like on inverted woden slabs. + +Build 4.0.0.247: + Christian: + Update FML: 12bc4ba + 12bc4ba Make console logging pass through a single logging thread to avoid concurrency problems with launchers and stuff + 8fccfa2 Overlooked chat message support *sigh*. Fixes wierd mods that communicate through custom chat messages rather than + a697d04 Rescan mod packages where there was a possible mod identified to try and make sure we load it. Fixes Matmos r16 + +Build 4.0.0.246: + Christian: + Update FML: 9d812f1 + 9d812f1 And handle the other way to globally register an entity + 869cf78 Fix up mods trying to register entities outside of modloading. Now you'll get a big fat warning for doing such a de + 186680c Fix CJB mod compatibilty for real. Gui container registration can now happen in the constructor. YUK! + 5aed6d9 Fix compatibility with ModLoader dispenser hook. Fixes a bug with IDispenseHandler too - though it means that inter + 3d7a665 Fix headless operation, hopefully + +Build 4.0.0.245: + Christian: + Update FML: 10a7169 + + 10a7169 Fix up trying to re-add stuff to the classpath when it's already there + 0eb7180 Fix out of range entity id handling for ModLoader mods a bit more. Thanks Herobrine mod! + b1b4c61 Expose GuiMerchantButton + 359cfb5 Log a big fat warning for the developer in the case the network version is not acceptable to the mod itself. (This can happen because of a bad range specifier for example) + +Build 4.0.0.243: + Christian: + Update FML: 8656fd5 + 8656fd5 Also, make missing entity not blow things up + 25a4dcb Fix entity tracker "update" packet not computing correctly. Should work now.. + +Build 4.0.0.242: + Christian: + Update FML: c90a853 + c90a853 Fix up addRenderer to catch exceptions from the basemod, also, addrenderer now supplies a list of the default renderers from MC for editing and will note the + 18069d8 Change download location for FML libraries to files.minecraftforge.net, add an encoding param to the javac command line to force utf 8 + a40f4a7 Add in random jar/zip files to the classpath. UGH this is so fucking ugly it's not funny. + 5b6460b Use a null socket and try to prefer the result of getLocalHost in the broadcast data. + +Build 4.0.0.240: + Christian: + Update FML: c0d1348 + c0d1348 Add in an ItemRenderer null check so FML can run render adding mods + 482c163 Inverted boolean logic. Grrr. + 221d113 ModLoader mob spawns use packet 24- build a bypass system so that they can still use it + f19f426 Remove debug statements + +Build 4.0.0.239: + Christian: + Update FML: 9dd9b6e + 9dd9b6e Fix keybindings not loading properly from gamesettings file after restart + +Build 4.0.0.238: + LexManos: Fix silk touch on Ender Chests. + +Build 4.0.0.237: + t.tomkins: + Update patches/common/net/minecraft/src/NetServerHandler.java.patch + + Update patches/common/net/minecraft/src/NetServerHandler.java.patch + + Server mods can now use noClip to push players through solid blocks without is being an invalid move and resetting the players position. + + Useful for creating semi-solid blocks like quicksand. + +Build 4.0.0.236: + LexManos: Fix Issue #140: Custom items not properly having texture applied for partical effects. + +Build 4.0.0.235: + Christian: + Update FML: acb0b21 + acb0b21 Fix so we always send received packets to handlers. Renders 250 filtering useless unless i can figure a way to intercept + +Build 4.0.0.234: + Christian: + Update FML: b17dd3c + b17dd3c Some tests + fa66ffa Fix trying to load the client side packet handler, when on the server, hopefully? + b106420 Fix up network negotiation - now it will disconnect with a nice error screen if there are missing mods on the client vs se + +Build 4.0.0.233: + Christian: + Update FML: c971adb + c971adb Fix missing addCommand call: thanks "spawnwolf test command" + d6326de Fix a couple of modloader mods. Thanks! + 483667c Some functionality fixes for @Instance injection. + 8bef512 Versions now have two flavours: the @Mod(version) which is the "programmatic version" and is used for all version relationship calculatio + 501009c Always for ML mods into "public" class accessibility - we don't call from the default package like ML does. + d3d4308 Add in a convenience method for getting a class instance based on existence of another Mod. Useful for soft dependencies. + +Build 4.0.0.232: + Christian: + Update FML: 2d9b88b + 2d9b88b And more mod sorting fixes. + 93d5934 Actually *use* the sorted mod list + +Build 4.0.0.231: + LexManos: Fix FillBucketEvent to actually fire. + +Build 4.0.0.230: + LexManos: Fix call to createTileEntity on blocks that extend BlockContainer. + +Build 4.0.0.229: + Christian: + Update FML: 180a279 + 180a279 Merge pull request #82 from Chicken-Bones/master + cf38461 The access transformer will now handle classes in packages. + 2c85539 Reverse the meaning of state in fml.modStates system property and fmlModState.properties - it was very derpy before. Now: true is enabled, false is disabled, as you would expect + d651189 Actually fix the corrupt JSON problem + f6a8cd5 Fix isModLoaded for "disabled" mods, and the toposort as well + 3bde961 A bit more information for dependency handling + 6396bea Fix up handing corrupt mcmod.info + 6bc6def Fix a problem with network ids + 7328610 Fix up version handling properly. Hopefully, and the error screen. + 9fcc452 Only active mods should have dependencies checked, and be sorted + 26302a6 Fix oddity with Screen, print a version out in the sorted list! + +Build 4.0.0.228: + Christian: + Update FML: 62dbfb3 + 62dbfb3 Fix metadata + +Build 4.0.0.227: + Christian: + Update FML: b91cbe9 + b91cbe9 Fix up versioned dependencies so they work a lot better, also add in a missing mods screen + +Build 4.0.0.226: + Christian: + Update FML: 93b8ba9 + 93b8ba9 Fix missing serverSendPacket + 23fdbbd Trading support for ModLoader - thanks Herblore! + d7a74d8 Close the jar, for cleanliness sake + 58e6dd9 Fix authors - it'll read both "authors" and "authorList" from the json now + 907cf2d More ML compatibility - fix up dragon mounts, thanks! + ee48a36 Fix up loading mods that are grandchildren of BaseMod + c6f362b Fix entity handling for ModLoader mods, thanks parachute mod! + +Build 4.0.0.225: + LexManos: Fix lighting issue for air related to 4096 fix. + +Build 4.0.0.224: + LexManos: Made helper hooks for EntityLiving and EntityRenderer hooks for Optifine + LexManos: Removed deprecated ISpawnHandler, that never worked in 1.3.2 so noone should be using it. See FML's replacement. + +Build 4.0.0.223: + Christian: + Update FML: 6e01c59 + 6e01c59 Merge ChickenBones pull into FML - adds player trackers + a0cbd37 More precise error handling when an invalid class is present + 12323e2 Fix textures not applying properly + 789a4a3 Fix up entity spawning - hopefully they won't warp anymore. + a87d045 Add in a helper on the server start event for adding commands to the server. + 82e9309 Move villager trading hook up a bit. thanks sengir. + 8deaa37 Fix LAN to try hard to find a real network interface, not just localhost most of the time + b5363e3 Update build to not bother building client jar. Universal is everything now. + +Build 4.0.0.222: + LexManos: Pickblock now replaces the currently selected item if it could not find a empty space. + +Build 4.0.0.221: + dvrabel: + Use original minecart drag co-efficients for regular carts. + + Since the amount of speed lost is 1 - co-eff, rounding 0.997 to 0.99 + makes a big difference to the distance a minecart will travel (less + than half of the distance in vanilla 1.25). + +Build 4.0.0.220: + LexManos: + Update FML to 304: + Client side only classes are properly annotated, and denied loading on server side + Added missing client side only ModLoader.getContainerGUI function + Guis work for FML mods now. + Fixed MLProp handeling of null info + Fix up dispenser handling, add in new params for the dispenser: breaks IDispenseHandler, sorry + Christian: + Update FML: 28a10ac + 28a10ac Null protection for ticks() -- probably should uses EnumSet.noneOf() but you can also use null now to stop ticking. closes #77 + c349d51 Automatic mod entity registration into the global entity list. This might break entity code out there, sorry, but this way for most entities you no longer need to manage the global mod entity stuff at all. just remove it. FML will take care of the rest (note, worlds won't load the old entities) + ef01745 Add in a utility function that *might* tell you the context you're running in on either client or server: FMLCommonHandler.getEffectiveSide + c97d6a6 Try and stop sendPacketToAllAround from being crashy + 2062273 Mods can add mod specific crash information to the crash report now Forge needs to implement MinecraftForge.getCrashCallable + 6e6436e Fix up dispenser handling, add in new params for the dispenser: breaks IDispenseHandler, sorry + 38f4a22 Fix up MLProp handling for null info + 3a8b047 GUIs working in FML for ML mods + 52483ee Support gui opening for Shelf mod + dce1cbc Updated MCPMerger to annotate unique classes with there sides, and SideTransformer to null out any class that is loaded on the wrong side. + +Build 4.0.0.217: + LexManos: Updated onItemUseFirst call to include the new hit vector information, and updated readme. + +Build 4.0.0.216: + LexManos: Added missing server side onBlockStartBreak hook, fixes Sheers. + +Build 4.0.0.215: + LexManos: Fixed Forge's 4096 block filler initalization. + +Build 4.0.0.214: + LexManos: Fix IndexOutOfBounds exception, Major derp. + +Build 4.0.0.213: + Christian: + Update FML: 46c563a + + 46c563a Expose more of the village construction parts for building village piece + 1380c2e Allow access to the type "ComponentVillage" + 71a3818 Documentation and registration for villager stuff + e3a343c Fix javadoc derp + 195b1a0 Villager trading, manipulation and village enhancement. Still WIP but sh + 69e66c3 Fix exception based logging to actually log the exception! + +Build 4.0.0.212: + Christian: + Update FML: + 885637c Fix up keyboard events for modloader + 90a7c8f Pickup notification + +Build 4.0.0.211: + LexManos: Changed order in which EntityItemPickupEvent is called, it will no longer be called if the 'delay' on the item is still active. Also EntityItems that are in the world with invalid item stacks will now kill themselves. + +Build 4.0.0.210: + mitchel.pyl: Adds a SkyProvider class that can handle the rendering of the sky + +Build 4.0.0.209: + Christian: + Move and add a few AT for forge: rail and leaves stuff mostly + Update FML (reorg some AT for FML vs forge): + 2171c0c Update fml_at with new transforms + 924a6f9 Fix derp where client sided packet handler would be created on a dedi server env + ad4cffb Add in support for "dummy" keybindings- ones that are added to the list but never do anything so the mod can handle it all themselves. + cf77ffb Don't complain about minecraft source code being in "net.minecraft.src". Derp. + b2fdcd7 Fix ModLoader add XP Smelting + 094ce2a Actually register client/server packetspecs as their correct sides! fixes #71 + +Build 4.0.0.208: + LexManos: Fix MC packet bugs for remote servers that use blocks with ids > 256 + +Build 4.0.0.207: + LexManos: Fixed bug in EntityMinecart that would cause them to fall through the ground at the bottom of slopes. + +Build 4.0.0.206: + Christian: + Update FML: + + e8cb2c1 Error if channel name is invalid - either too short, or too long + 9c6c56c Add a connect and read timeout for downloading libraries. This should really stop the "waiting forever" at startup screen. + 0d5affe GIANT FAT WARNINGS if you use any code in net.minecraft.src or any subpackage thereof. Get it out of there, now! + 13f210f Fix up ML GUI ticking. Thanks sharose! + +Build 4.0.0.205: + LexManos: Fixed EntityEvent.CanUpdate + LexManos: Fix EntityItemPickupEvent 'pop' sound spamming. + LexManos: Added timeout to MC's download of sound resouces. + LexManos: Update WorldProvider.path diff. + +Build 4.0.0.204: + Christian: + Update fml: + c61ad51 Organize ALL the imports + c0842b0 Expose the server in serverstart event + 2851079 Fix up directory injection of log file locations + 5197524 Refer to FMLLog, not FMLRelaunchLog + 78efd1a Tidy up server launch - reflection not required anymore + eee0a99 Also, log what the directory discoverer finds for mcmod.info + 10c96c3 And log the exception if it has a problem reading the jar file + 07cc3fb Fix jar loader failing to inject any metadata at all if mcmod.info is not found. + e31f143 Change install.cmd to install.bat, why, cuz people are .... Updated readme to mention needing server and client. + +Build 4.0.0.200: + LexManos: Allow white space in the property name. + Christian: + Update FML to include some ML compatibility fixes. Minimap should run, assuming he + recompiles against standard ML interfaces. + This updates to official MCP 7.2, so you'll need to rebuild your workspace (some + small but important patches to MCP went in) + +Build 4.0.0.199: + Christian: Update FML, make the .sh scripts executable + +Build 4.0.0.196: + LexManos: Update FML to 285 + +Build 4.0.0.194: + draake: Added additional sound events to allow manipulation of a sound source at its creation. + draake: Removed SoundSetListenerEvent implementation. + +Build 4.0.0.193: + LexManos: Fix potential index exception in ForgeDirection + LexManos: Whops + +Build 4.0.0.192: + LexManos: Rename Orientation to ForgeDirection {Damn MCP naming a class Direction already} + +Build 4.0.0.191: + LexManos: Update FML again for mod screen blending fix. + +Build 4.0.0.190: + LexManos: Updated Orientation with new helper functionality. + LexManos: Fix ItemBlock placement, should fix RP Deployers placing blocks 1 tile down. + LexManos: Seperated block placmenet logic in ItemBlock as immibis requested: https://github.com/MinecraftForge/MinecraftForge/pull/110 + LexManos: New Command event. + LexManos: Fix issue with default getContainerItemStack + LexManos: Add render distance property to TileEntity. + +Build 4.0.0.189: + LexManos: Updated FML, should fix the new included eclipse workspace. + +Build 4.0.0.188: + LexManos: Cleanup repo, delete old code + LexManos: Update FML to 278, MC 1.3.2 + LexManos: Cleaned up forge scripts, and updated AT config for 1.3.2, deleted any reference to server specific code. + LexManos: Updated patches to 1.3.2 + LexManos: Fixed bug in DimensionManager that would cause Index error for custom dimensions, also made WorldProviders aware of what dimension they are. + +Build 4.0.0.187: + LexManos: Readded patch to allow for use of custom teleporter for transfering dimensions. + +Build 4.0.0.186: + LexManos: Update FML with entity spawning fixes + +Build 4.0.0.185: + LexManos: Updated FML, should fix 'derp?' RuntimeError + +Build 4.0.0.184: + LexManos: Fix PickBlock 'ghost item' bug when picking with a item selected. + +Build 4.0.0.183: + LexManos: Update FML, Tons of bug fixes and proper merging of client and server. + LexManos: Delete Forge server projects + LexManos: Update debug settings to use Client's internal MinecraftServer.main to debug dedi-server env. + LexManos: Update release script to create universal binary distro. + +Build 4.0.0.182: + j.marini: Fix bug where default bonemeal behavior wouldn't trigger. Only return if the event was canceled. + +Build 4.0.0.181: + LexManos: Update FML, new AT features, and error info. + +Build 4.0.0.180: + LexManos: Fixed ItemRenderer not grabbing custom texture files properly for block items + LexManos: Updated FML + LexManos: Delete dead IGuiHandler + LexManos: Update AT config to expose some block functions and a few extras. + +Build 4.0.0.179: + LexManos: Should fix commands.py patching not working properly first run. + +Build 4.0.0.178: + LexManos: + Updated FML, Should fix a lot of installing issues for MCP. + Made patches error and not apply if the target file is not found. + Updated Event Transformer to add a default constructor... + Fixed 4096 setup code in MinecraftForge.initalize() + +Build 4.0.0.177: + LexManos: Fix issue in install script for users with spaces in there paths. + +Build 4.0.0.176: + LexManos: Update FML, server should run now. Still issue with NPE with no mods installed. + LexManos: Updated FML, fixed custom tool hooks, and spawn protection/player reach hooks. + +Build 4.0.0.173: + LexManos: Fixed Diemsnion API related NPEs, and updated FML to 231. + +Build 4.0.0.172: + LexManos: Get rid of all old patches + LexManos: Added a better eclipse workspace for deving Forge + LexManos: inital push of updated setup script and cleaned up forge.py + LexManos: Dump old mcp config + LexManos: New Start.java which allows the user to login using a legit account for testing on servers. + LexManos: Committing inital work on generating sanitized MCP conf data. + LexManos: Add the direct copied files from MCP conf + LexManos: Add newids to that list + LexManos: Helper function grab all unique searge names that are shared client and server + LexManos: Added merging fields/methods/param csv files. And updated FML to 197 + LexManos: Remove eclipse files from old eclipse projects + LexManos: Updated build.py + LexManos: Update FML + LexManos: + Moved logo to new client folder + Added blank Access Transformer config for Forge + Implemented version storage + Added basic dummy FML mod container to remove the need for mod metadata file + Added beggining work on Forge event system + Updated and moved EnumHelper + LexManos: Updated update_patches.py, and made first patch! + LexManos: Moved paulscode to new client folder + LexManos: First push of some small patches and introduction of basic events. As well as the first cases for Forge's Access Transformer + LexManos: Missed a file + LexManos: Moved in OreDictionary stuff to new system. IOreHandler is replaced by OreDictionary.OreRegisterEvent + LexManos: Renamed MinecraftForge.eventBus to EVENT_BUS as it's constant. + LexManos: Implemented BonemealEvent (IBonemealHandler replacement), and bonemeal making grass. + LexManos: Implemeneted adding grass seeds, as well as IShearable for BlockTallGrass + LexManos: More progress on converting patches from 1.2.5 + LexManos: Some more patch migration before bed. + LexManos: + More patches converted. + Refactored some of the events to be a better hiarachy. + LexManos: A bulk of more patches converted, updated fml + LexManos: Moved some files. + LexManos: More work, moved over packet stuff, need to change everything to use new NetworkMod system + LexManos: More conversions, added all the entity living events. + LexManos: Finished World and EntityMinecart patches. + LexManos: Removed a lot of old files + LexManos: Updated Client side patches for the new Merged MCP workspace. + LexManos: Nope, unneeded patch + LexManos: Cleaned up some more, fixed infinite loop with cancelable annotation, Client now compiles and enters world just fine. + LexManos: Delete conf, server compile, update fml, and fix install script. + LexManos: Copy forge files over in install. + LexManos: Update fml + LexManos: Moved over Configuration + LexManos: Fix oreientation bug causing levers not being able to place. + LexManos: Added new pick block hook for blocks and entites to allow for better grained control of the result. + LexManos: Include Forge's AT config into redist zips. + LexManos: Update fml, impelemnted Diemsnion API stuff. + LexManos: Updated FML, updated patches + +Build 3.4.9.171: + Christian: Normalize the repo + LexManos: Rebase + LexManos: Bumped version to 3.4.9, final commit for 1.2.5. + +Build 3.3.8.170: + someh4x0r: Fix BlockTrapDoor.disableValidation on server + +Build 3.3.8.164: + LexManos: Should fix NPE on older ISoundHandlers EXA: http://minecraftforge.net/forum/index.php/topic,759.html + +Build 3.3.8.163: + Christian: Update fml with recent tick fix + +Build 3.3.8.162: + truebrain: + -Add: sync serverPos[XYZ] on spawning of entity + + This fixes the issue that, up to 400 ticks from getting in range, + entities have a wrong offset (because server and client don't agree on + the position to calculate relative updates from) + truebrain: -Fix: check for null where needed + truebrain: -Codechange: applied LexManos' request + +Build 3.3.8.161: + LexManos: Fixed a improper rounding in EntityMinecart + LexManos: Made WorldGenMinable metadata sensitive. + +Build 3.3.8.160: + Christian: Update fml to 175- fixes some weird ticking behaviours for ML mods, adds in new ticking behaviours for smart mods + +Build 3.3.8.159: + LexManos: Moved licenses and credits into install folder. + LexManos: Removed symlink files, shouldn't be in the repo. + LexManos: Updated some remaining scripts to call there python versions. + +Build 3.3.8.158: + LexManos: Fix furnaces not smelting the proper number of results after the first one. Damn you tahg, fix this! Keeps falling through my cracks. + +Build 3.3.8.157: + Christian: + Fix server check "blockHasTileEntity" used to determine if getDescriptionPacket + is sent for the block to be metadata sensitive. Thanks iChun! + +Build 3.3.8.156: + Christian: Fix ghost players who appear if they don't have the 4096 fix installed + +Build 3.3.8.155: + LexManos: Fix container creative to not add Blocks above 256 unless it's told to. + +Build 3.3.8.154: + Christian: + Fix world provider behaviour: WorldProvider.byDimension should always return + a new instance- otherwise you can end up with worlds overwriting one another + +Build 3.3.8.153: + LexManos: Fixed isBlockReplaceable and placing ItemBlocks' + +Build 3.3.8.152: + LexManos: Fucking spelling nazis + +Build 3.3.8.151: + LexManos: Fix bitmask bug related to 4096 in multi-block change. + LexManos: Updated FML to build 153 + +Build 3.3.8.150: + LexManos: Fix NPE related to server/client not having block ids synced properly. + LexManos: Add option to randomize potential spawn locations to have a more randomly dispersed spawn patern, useful for games like FTB vs. + LexManos: Added configuration option to disable the darkroom mechanic. For servers who wish to not risk destruction at one broken pipe. + +Build 3.3.8.148: + LexManos: FML 150, more tick related work. + +Build 3.3.8.147: + LexManos: Close inputstream + +Build 3.3.8.146: + LexManos: More bugfixes related to portal generation. Should be all now. + +Build 3.3.8.145: + LexManos: Some 4096 I missed in custom renderers. + LexManos: Updated to FML 149, should fix all ticking issues with ModLoader mods + LexManos: Fix Vanilla bug where nether portals would not work above 127 + +Build 3.3.8.144: + LexManos: Updated to FML build #142, fixed FML compilation issue causing missing files when reobfing. + +Build 3.3.8.143: + LexManos: Updated release script to automatically inject version info into .info file. + +Build 3.3.8.142: + LexManos: Updated to FML 141, should fix ticking issues with certian mods, and crash issue with TropiCraft + +Build 3.3.8.141: + LexManos: Bumped version number for new IEntityLiving interface. + +Build 3.3.7.140: + pahimar: Typo in the new EntityLivingHandler code + +Build 3.3.7.139: + LexManos: Made setFailsafeFieldValue public for Rob + LexManos: Implemented enchanced EntityLiving events. Thanks Atomic for parts of it. + +Build 3.3.7.138: + LexManos: Few more cases for 4096, thanks TrueBrain + +Build 3.3.7.137: + LexManos: Forgot Configuration in 4096 change. + +Build 3.3.7.136: + LexManos: Configurable kick message when you don't have forge. + LexManos: Initial 4096 fix based of mDiyo's work. + +Build 3.3.7.135: + LexManos: Fixed bug in new entity sound hook. + +Build 3.3.7.134: + LexManos: Updated to FML #135 {Fixed ModList rendering issue, and incorrect arument on crafting hook} + LexManos: Added new ISoundHandler.onPlaySoundAtEntity hook. Should be backwards compatible with any older ISoundHandler implementations. + LexManos: New onChunkUnload event for TileEntities, for psxlover. + LexManos: Bumped version number to 3.3.7 for full release. + +Build 3.2.6.132: + LexManos: Fixed concurent modification exception in the ore registry. + +Build 3.2.6.131: + LexManos: Fix for potential concurancy modification exceptions + +Build 3.2.6.130: + LexManos: Fixed possible NPE when blocks havent fully initalized yet. + +Build 3.2.6.129: + LexManos: Updated FML to 132, TextureFX fix, and bumped revision to 6. + +Build 3.2.5.128: + LexManos: Added bouncer functions for functions we removed. Makes MagicLauncher shut up about missing functions {not actually missing}, and therefor makes users stop thinking its a life or death error. + +Build 3.2.5.127: + LexManos: New Ore Dictionary recipies. Allows for simpler Ore Dictionary integration with recipies. IOreHandler should no longer be used. + +Build 3.2.5.126: + LexManos: Updated to FML 130 + LexManos: Added hackish workaround for Tessellator.textureID to provent crashing while we wait for Optifine to update. + +Build 3.2.5.125: + LexManos: Server side of ITextureProvider for Block/Item for compilations sake. + +Build 3.2.5.124: + LexManos: Backwards compatibility fix for ITextureProvider + +Build 3.2.5.123: + LexManos: Updated FML to 121 to fix world tick issues. + +Build 3.2.5.122: + LexManos: Updated MCP mapings. Moved the bulk of custom logic from RenderItem to ForgeHooksClient. + LexManos: All Items and Blocks now implement ITextureProvider, and have a setTextureFile(String) function, to support cleaner code in Mods and in Forge. + LexManos: Tag each Tessellator with it's associated texture ID. + +Build 3.2.5.121: + LexManos: Added kick info to disconnect event, updated to FML 120, keybindings, better image rendering in ModList ui. + +Build 3.2.5.120: + LexManos: Fix bug in cart functions where would always return null. + +Build 3.2.5.119: + LexManos: Fixed some inverted logic causing tress to not gen correctly. + +Build 3.2.5.118: + LexManos: Update FML to 117 + +Build 3.2.5.117: + LexManos: New Hooks addedf for custom tree/leaves. And better interaction with trees growing and rerplacing certain blocks. Should allow for ExtraBiomes to behave nicer, and RedPower to make there blocks un-breakable by trees. + LexManos: Also, new hook in last commit for blocks to determine if they are able to be replaced by ores, for any mod that adds new world gen. Bumped revision up. + +Build 3.2.4.116: + LexManos: We now bundle CodexIBXM from PaulsCode. See http://paulscode.com http://www.paulscode.com/forum/index.php?topic=4.0 and the included license files for more info. + LexManos: Updated to FML 115, added Forge Logo to the client dist, and in-game ModInfo page. + +Build 3.2.4.115: + LexManos: Updated to FML build 114 + +Build 3.2.4.114: + LexManos: Included fmlversion.properties in the release zips. + +Build 3.2.4.111: + LexManos: Added FML ReadMe, Credits, and License to release zips + +Build 3.2.4.110: + LexManos: Update FML to 92, this include full client side support, ModLoader nolonger needed. + LexManos: Added new source clean step to fix linux vs windows astyle issues. + LexManos: Cleaned up names for Minecraft Forge's text files so they dont clash with other mods. Fixed up the release script to include the license text, and executable permissions for install.sh. + LexManos: Added FML mod info file + LexManos: Updated MCP Mapings and patches. + LexManos: Updated patches for FML, moved some extranious code to Forge classes instead of patches. + LexManos: Updated to FML Build 95 + +Build 3.2.3.108: + LexManos: Bump version number for official release. + +Build 3.1.3.107: + LexManos: Fixed AudioMod compatibility with MultiMC style launchers. + +Build 3.1.3.106: + LexManos: Bit masked the entity ID to change the range from -127-127 to 0-255 + +Build 3.1.3.105: + LexManos: Updated MCP Mapings + +Build 3.1.3.104: + LexManos: Added forge identifier to statistics collection. + +Build 3.1.3.103: + LexManos: Fix for furnaces not smelting the final item in full stacks. + +Build 3.1.3.102: + CovertJaguar: static final vars are inlined during the compile step, making the version variables useless at runtime. + LexManos: Fixed Levers droping off the backs of stairs client side. + +Build 3.1.3.101: + LexManos: New block hooks for creating custom beds. + +Build 3.1.3.100: + cpw: Update to FML 74: fixes an important issue with MLProp + +Build 3.1.3.99: + LexManos: Bumped revision to 3 + +Build 3.1.2.98: + LexManos: Fixed Vinella mob spawning bug that prevented mobs from spawning on the top most chunk. Added new Block function to determine if a Mob can naturally spawn on the block. Add new hook to allow for special case handeling on natural entity spawn. + +Build 3.1.2.97: + LexManos: Fix for loading Minecart Entities in worlds that were last accessed before Forge was installed. + +Build 3.1.2.96: + LexManos: Moved MLMP compatibility functions to ModCompatibilityClient, and fixed issue where no vehicles would be spawned. + +Build 3.1.2.95: + LexManos: + New ISoundHandler interface, useful for adding custom sounds and dealign with sound based events. + Includes basuic AudioMod compatibility. + +Build 3.1.2.94: + LexManos: Fixed vinella bugs when trying to access chunk information with a y < 0 + +Build 3.1.2.93: + LexManos: Updated to FML build #73 + LexManos: Added MLMP hook invocation for vehicle spawn and opening GUI's to provide compatibility for clients that have both ModLoaderMP and forge installed. + +Build 3.1.2.92: + LexManos: Exposed TileEntityData packet to TileEntities. And added helper sender function. + +Build 3.1.2.91: + LexManos: Added metadata sensitive Chunk constructor for Bioxx + +Build 3.1.2.90: + LexManos: Added MinecraftApplet.clas to the force output list because users cant understand the concept of only replacing files they are told to. And bumped version to 3.1.2 + +Build 3.0.1.89: + LexManos: made the ID in PacketEntitySpawn unsigned. + LexManos: Added generic packet sending function to MinecraftForge class. + LexManos: Fixed shift-clicking issue with items that utilized the new ItemStack sensitive smelting system. + LexManos: Implemented a 'small' packet system for those mods that need to send rapid, small data packets and who are concered about the overhead of the 250 payload packet. It uses packet 131. + +Build 3.0.1.88: + LexManos: Implemented RichardG's Paged Achivement list GUI modifications. + +Build 3.0.1.87: + t.tomkins: Edits enable use of EntityPlayer.canHarvestBlock (for PlayerAPI) when the ToolClass has no rules for the block. + +Build 3.0.1.86: + LexManos: MAX_ENTITY_RADIUS Now works on the Y axis + LexManos: Respawning in dimensions should now properly set the dimension ID for the new player instance. + +Build 3.0.1.85: + LexManos: Fix for vanilla crash for chunk generation with block ID's above 127. + +Build 3.0.1.84: + cpw: Update fml to build 68: fixes a ticking issue and soft vs hard dependencies + +Build 3.0.1.83: + LexManos: + Changed entity Forge spawning code works to be more in line with normal spawning: + (World, double, double double) constructor is nolonger called, normal (World) constructor, and setPositionAndRotation is called afterwords. + yaw, pitch, and yawHead is sent (if the entity isn't a EntityLiving yawHead isn't used) + The datawatcher data is also sent like a normal Entity. + +Build 3.0.1.82: + LexManos: Fixed bug in BlockFire's old fields. + LexManos: Added variable entity radius to World, to be used if a mod make really large entities. + +Build 3.0.1.81: + LexManos: Fixed TileEntity bug that was causing Ghost tile entities. + +Build 3.0.1.80: + LexManos: Multi-pass item rendering now uese proper render pass number. + +Build 3.0.1.79: + LexManos: Added slope velocity hook for Covert + +Build 3.0.1.78: + LexManos: Fixed bugs in the Dimension transfer code, Now displays proper join message, and respawning in dimensions other then 0 works. + +Build 3.0.1.77: + CovertJaguar: Added a function for checking block harvest levels without having to resort to reflection. + LexManos: Try to fix jenkins build. + +Build 3.0.1.75: + LexManos: Added ItemStack sensitive burn time hook. + +Build 3.0.1.74: + LexManos: + Added new Item function to allow for multiple rendering passes. + Fixed a vinella bug where item layters would not line up for EntityItem's + Added new IItemRendere helper flag for 3d block translations. + +Build 3.0.1.73: + cpw: Update fml to 62- provision a server console command and fix mod loading order + cpw: Update FML for new hooks: onPlayerLogout and onPlayerDimensionChanged + LexManos: + Rewrote IItemRenderer to be a more generic and expandable version, Breaks previous interfaces, but this should be the last time the interface changes. + Implemented the MapRender hook ChickenBones requested. + +Build 3.0.0.72: + cpw: Fix separated login/announce handler + cpw: FML 59: don't send a zero length register packet, add mod channels + +Build 3.0.0.71: + cpw: Update to FML 57: fixes FML not calling it's login handler code + +Build 3.0.0.70: + LexManos: MCP Conf update for 1.2.5 + LexManos: Initial patch update to FML and MC 1.2.5 + LexManos: Deleted patches + LexManos: Fixed update_patches.py to now delete old patch files that have no working src relation + LexManos: Updated repo scripts to python, allowing more flexability and less code duplication. + LexManos: Finished up converting the source dist install scripts to python. Fixed a couple issues with the other scripts. + cpw: Update patches for FML fix. + cpw: Fix release script to import "inject_version" + cpw: Add in recent fml with fixes + cpw: Delete the old one too + LexManos: Rewrote the login code to delay full login untill forge fnishes negotiating network IDs. Should fix entities not having proper ID mapings. + LexManos: Added ChickenBones's request ITextureLoadHandler hook + LexManos: Updated patch for mapings + LexManos: Implemented ISaveEventHandler as ChickenBones's requested. + LexManos: Add server spawn protection config option as per someh4x0r's request. + LexManos: Couple of output fixups and added script to be executed by Jenkins during the build. First test build. + LexManos: Changed IGuiHandler's signature to be compatible with both sides. Took out ModLoaderMp reference in README. + +Build 2.0.0.68: + LexManos: Fix some shadowing warnings. + LexManos: Some cleanups to update_patches, should run properly on any system now. + LexManos: Add IChunkLoadHandler.canEntityUpdate() function to allow entities to update while in custom loaded chunks. + LexManos: Implemented a chat event handler system to allow for manipulating and handeling the various chat/command events. + +Build 2.0.0.67: + LexManos: Changed update_patches script to use python for the bulk, Prevents making 1800 temporary useless files. + LexManos: Fixed camelcasing in IConnectionHandler, ment to do during first upgrade. + +Build 2.0.0.66: + LexManos: Fixed typo bug in MinecraftForge.getEntityTrackerInfo + +Build 2.0.0.65: + LexManos: + Updated to MC: 1.2.4 MLMP: 1.2.4 v1 + Removed all code that was marked as deprecated, IE: ICustomItemRenderer, and the functions in Configuration + +Build 1.4.1.64: + LexManos: Reworked EntityPlayerMP.openGui to hopefully be more compatible with PlayerAPI + +Build 1.4.1.63: + LexManos: Cleanup the EffectRenderer patches, Allows custom food items to have the proper particals. Cleaned up special case class in favor of basic java generics. + +Build 1.4.1.62: + CovertJaguar: Added the ability to define custom categories for config files. + CovertJaguar: Removed createCategory() function and changed it to create categories as they are encountered. + LexManos: Updated MCP mapings + LexManos: + Fixed a couple of bugs in the Configuration categories pull request. + Added helper functions to Property for standard int/boolean usage. + LexManos: Added rider sit pull request. + +Build 1.4.1.61: + LexManos: Fixed NPE on CustomPayload packets with 0 data + LexManos: Changed logic in Configuration to allow for extended block ID mods easier access to change that. Just use reflection to change Configuration.configBlocks to whatever length you want. + LexManos: Implemented Pheenixm pull request allowing for more control over the camera. + LexManos: + Added MinecraftForge.isClient() function that returns true if you are in the Minecraft Client, (As defined as net.minecraft.client.Minecraft existing) false otherwise. + Fixed a typo on PacketEntitySpawn that caused speed to be assigned improperly. + A little code cleanup. + +Build 1.4.1.60: + LexManos: Fixed a few typos that CovertJaguire found in The Forge entity handeling code. And re-wrote the connection sequance so that clients get a list of mod ids before anything else. + +Build 1.4.1.59: + LexManos: Readded the deprecated interface ICustomItemRenderer. Added method to enforce deprecation cross Minecraft versions. + +Build 1.4.1.58: + newthead: Added new item render interfaces to Forge client + newthead: Added hook for rendering equipped items + newthead: Clarified render option for inventory items + newthead: Added remaining item render hooks, and fixed a static method access + newthead: Added render code for inventory items and entity items. + newthead: Cleanup/fix item render hooks + newthead: Added additional flag for rendering entity item bobbing + newthead: Merged item rendering interfaces into a single IItemRenderer + newthead: Updated render hooks and registration to use the IItemRenderer interface + newthead: Modified patches to use new item render hooks + newthead: Merged item rendering interfaces into a single IItemRenderer + CovertJaguar: Fix for network code. + newthead: Changed render hook checks to all use ItemStack instead of itemID + newthead: Reverted vanilla texture binding to an explicit if() statement based on item ID + LexManos: Fixed dyes showing twice in creative list + LexManos: Bumped version number to 1.4.1 + +Build 1.4.0.57: + LexManos: Fixed tile entities not properly being removed for blocks that do not extend BlockContainer, or override BlockContainer.onBlockRemoval + +Build 1.4.0.56: + LexManos: Fixed excessive need for user interaction in the install script. + LexManos: Fixed a class to a deprecated method to fix tool effectiveness. + +Build 1.4.0.55: + LexManos: Reworked the workspace scripts so the build should fail on compile errors. This build should fail as a test. + LexManos: Fixed IOException not found error in CovertJaguires pull request. Updated scripts so that build should now be fixed + +Build 1.4.0.53: + CovertJaguar: Added throws clause to ISpawnHandler functions to keep the functions consistent with similar functions and increase ease of use. Exceptions are already being caught everywhere these functions are called, so no further changes required. + LexManos: Fixed onTakenFromCrafting hooks to be called once per stack taken. Sadly due to how shift-click is handled, the passed in item stack is not garenteed to be the stack that the result was added to. + +Build 1.4.0.52: + LexManos: Fixed forgotten type identifier in clean_src + LexManos: New EntityInteract hook for handeling player vs entity interaction on the global level vs the item level. + LexManos: Fixed sides of grass not getting biome colors + LexManos: Fixed type on patch conversion messing up tile entities + LexManos: Proper fix for SDK's invalid bit shifts of parameters for chunk population + LexManos: Made BlockStairs, BlockFarmland, and BlockStep have proper sided-solidity. Can now place torches on any solid side of those blocks. + +Build 1.4.0.51: + cpw: + Fix worldgen doubleshifting in ModLoaderMP until SDK fixes it. + + This means mods can generate surface and nether in SMP again. + +Build 1.4.0.50: + eloraam: Fixed a merge error. + LexManos: + New script to fix decompile differences between windows and linux/osx. + Removed the MLProp special case, no longer needed as MLProp decompiles fine. + Updated patches to work on all systems. + Added warning and exit if the user does not say yes to the cleanup. + +Build 1.4.0.49: + eloraam: MCP Update. Fixed a few small bugs in the linux scripts. + +Build 1.4.0.48: + LexManos: Fixed the inverted logic in the missing mod check. + LexManos: Fixed a wrong argument for dimensions, should fix entity tracking. + LexManos: Custom dimensions should now honor the canRespawnHere function for respawning. + +Build 1.4.0.47: + LexManos: Fix up Configuration ids to only work for 256 ids until Jeb finishes the 4096 conversion. + +Build 1.4.0.46: + LexManos: Fixed a bug where vines were not shearable. + +Build 1.4.0.45: + CovertJaguar: Fixed a minor bug with the Minecart Dictionary. + +Build 1.4.0.44: + LexManos: 1.4.0 official release. + +Build 1.3.4.42: + LexManos: Updated to MLMPv3 + +Build 1.3.4.41: + LexManos: Hack-fix for players getting stuck during logins. Detailed fix when bug is actually tracked down. + LexManos: Implemented ChickenBone's Chunk handler + +Build 1.3.4.39: + LexManos: Fixed client side bonemeal on grass only making flowers + LexManos: Updated to latest MCP + +Build 1.3.4.38: + LexManos: Fixed MLMP thinking Network mods need a client side when they dont. + LexManos: Fixed a generic tile entity line i missed on the server side. + LexManos: Removed improper tile entity negation + +Build 1.3.4.37: + LexManos: Updated to SDK MLMP v2 + +Build 1.3.4.36: + LexManos: Fixed missing double array in MLMP's packet server side. Also, fixed the creative code again, brain failed. + +Build 1.3.4.35: + LexManos: Got my comparitor turned around + LexManos: Updated to SDK's 1.2.3 MLMP, Mush cleaner, no longer supporting Flan's. + +Build 1.3.4.34: + LexManos: Updated gitignore, fixed digging particles, fixed creative container showing to many blocks, fixed wrong argument for custom renderers. + +Build 1.3.4.33: + LexManos: Updated MCP's patch files. Fixes hangs on running MC as applet. + LexManos: ItemBlock now implements ITextureProvider so that blocks with custom textures will showup properly when being held. + +Build 1.3.4.32: + LexManos: Fixed NPE in the login handler + LexManos: Fixed directory issues in the setup script + LexManos: Cleanup of ModLoaderMP and proper update to the latest version of ModLoader + +Build 1.3.4.31: + LexManos: Fixed missing space in linux scripts + LexManos: No longer mark forge packets as chunk packets. + LexManos: Updated MCP mapings files + +Build 1.3.4.30: + LexManos: Updated scripts so that we use Fernflower now. Added a small python script to download fernflower for the user. And the scripts exit out if it fails. + LexManos: Deleted modLoaderMP.patch, unneeded now that we use Fernflower + LexManos: Updated eclipse project for MCP 1.2.3's inclusion of the Start folder in the src folder. + LexManos: Updated The Json enum helper to point to the new packaged names. And fixed a casing change in ModLoader + LexManos: Made mod_MinecraftForge extend NetworkMod, and changed NetworkMod to extend BaseModMP server side. + LexManos: Fixed unneeded imports in ForgePacket + LexManos: removed unneeded import, and fixed casing of args in IShearable + LexManos: Deleted EntityPigZombie patches, Who needed these? Why could you not do this via reflection? + LexManos: Conf for 1.2.3 + LexManos: Same patch diff, files. + LexManos: No longer needed, bug was fixed + LexManos: Client side initial patch update to Fernflower and MC 1.2.3 + LexManos: Bit of a cleanup of the client patches + LexManos: Couple of things I missed client side + LexManos: Deleted unneeded server side patches, Packet250 bug was fixed. And the others got moved to AnvilSaveHandler + LexManos: First push of server side patch updates for 1.2.3. MLMP is still not updated so it does not include those changes, and will not compile. + LexManos: Small imports cleanup + LexManos: Missed damageCarryover on the server + LexManos: Made the scripts forge updating md5s/mcp/names + LexManos: Updated for MLMP 1.2.3 v1 + +Build 1.3.4.29: + LexManos: Bumped version to 1.3.4, final commit for Minecraft v1.1 + +Build 1.3.3.28: + LexManos: Fixed bug in build script that caused the Minecraft/MinecraftServer classes to not be included. + +Build 1.3.3.27: + LexManos: Client side of the new Dimension support. + +Build 1.3.3.26: + thedeveducer: Fixed spelling mistake ('Frequancy') + LexManos: Fixed ITextureprovider for multi-textured items when rendering on the GUI. + LexManos: Fixed compile error for the spelling pull request. + +Build 1.3.3.24: + LexManos: Fixed up Shearable blocks to play nicer with sub-classes. + LexManos: Fixed line endings in windows update_patches + LexManos: Deleted Deprecated interfaces. + LexManos: Ran AStyle over forge code for uniform code formatting, and refactored old code a bit to have cleaner names. + +Build 1.3.3.23: + connor: People may think the server needs the mods to continue and not the client. + +Build 1.3.3.22: + LexManos: Fixed a bug in update_patches that caused apostrphies to be removed. Causing the last patch to be generated incorrectly. + +Build 1.3.3.21: + LexManos: + Initial attempt at server side extra dimensions support. + Mods must register there dimensions on load, by calling DimensionManager.registerDimension(uniqueID, WorldProvier, boolean) + Mods are resposible for writing there own transporter blocks. + Initial commit, needs some more testing and design thoughts. + +Build 1.3.3.20: + LexManos: Added new Item.getIconIndex context sensitive version. + LexManos: Implemented a response to Methuselah96's Arrow hook request. See IArrowNockHandler and IArrowLooseHandler + +Build 1.3.3.19: + LexManos: Changed the layout of the missing mods gui a little. Still needs a lot of work to look good. + LexManos: Marked forge packets as 'chunk' packets. Giving them a higher priority making the initial mod list check more reliable. + LexManos: Introduced some debugging info into Forge packets. And a debug flag in the client and server packet handlers. + LexManos: Moved Client side EntityPlayer.openGui work code to EntityPlayer for compatibility between player API. + +Build 1.3.3.16: + eloraam: Linux scripts fixed, TE bug fix. + eloraam: Fixed linux scripts, fixed TE bug. + +Build 1.3.3.15: + LexManos: Exposed getters for NetServerHandler.playerEntity and NetworkManager.netHandler + LexManos: + Introuduced NetworkMod, a BaseMod extension and replacement for BaseModMP. All mods that are designed to work in the server environment should extend this. + NetworkMods will be assigned unique ModIDs by the server, and those IDs will be sent to the client upon connection. + Refactored Forge packets to the net.minecraft.src.forge.packets package. And introduced the base ForgePacket class. + Added initial ModList request/response. + LexManos: + Expanded the Mod check to test for missing mods and display a GUI screen for the client if it is missing any. + The client now downloads NetworkMod ID's from the server upon connecting. + LexManos: Implemented a generic EntityPlayer.openGui system, and the network backend for it to work on server and client. + +Build 1.3.3.14: + LexManos: + Fixed up the install scripts. The linux script no longer needs sed. Should fix some issues on OSX's without gsed. + Also forge sources will only be copied to sides that were downloaded. + Also, install scripts now prompt for input to cleanup. + +Build 1.3.3.13: + LexManos: Split mod_MinecraftForge so that Clients arnt required to have MLMP installed if they don't use a MP mod. + +Build 1.3.3.12: + LexManos: Update MCP Mapings + LexManos: Small update to update_patches.bat, to skip patch reject files. + LexManos: Bumped version to 1.3.3 + +Build 1.3.2.10: + LexManos: + Refactored so that ISpecialArmor items do not see the factor of 25 on the damage. + The value returned form ISpecialArmor.getProperties is now copied, so mod items are free to re-use there return values. + +Build 1.3.2.9: + LexManos: + Reworked ISpecialArmor to provide better functionality as requested by IC2 devs. + Armor can now be prioritized over other armor. + It can deal with damaging it's own itemstacks. + Also impkemented the ISpecialArmor.getArmorDisplay + +Build 1.3.2.8: + LexManos: Lowered the amount of build log spam, and fixed the inject_version script for use on other systems. + LexManos: Fixed ITextureProvider support for items that have multiple colored overlays. + LexManos: Added Item.onLeftClickEntity hook. Used to intercept and cancel the 'attack' action within items. + LexManos: Implemented item.onBlockStartBreak call for creative single player. + LexManos: Implimented a new IShearable system. This allows mods to create both new shear-like items/blocks. As well as new Entities/Blocks that work with other mod's shear-like items. + +Build 1.3.2.7: + LexManos: Missed a caluse, ISpecialArmor should work as intended now. And not destroy Armor instanatly. + +Build 1.3.2.6: + LexManos: + Implmented Sengir's IPickupHandler. + http://www.mod-buildcraft.com/forums/topic/hook-intercept-item-pickups-by-player-entities/?view=all + +Build 1.3.2.5: + LexManos: + Merged in the change to ISpecialArmor to introduce the DamageSource argument. + As well as the concept of damage absorption vs reduction. + +Build 1.3.2.4: + LexManos: + Created a Entity tracking ans spawning system. + Mod creators should call MinecraftForge.registerEntity to register a entity. + All entity ID numbers are Mod Unique. Meaning two mods can have Entity #1. + Added client and server side packet handlers for the 'Forge' channel. For use in internal packets such as the new Spawn packet. + Updated the build scripts to copy over unique server source files now that there actually are some. + For modders: + If you have a entity that used MLMP's 'owner' system, you should have your entity implement IThrowableEntity + If you have a entity that implments MLMLP's ISpawnable, you should implement ISpawnHandler. + They provide the same functionality, just in a cleaner, more orginized way and will be the method used when we eventually drop MLMP. + +Build 1.3.2.3: + LexManos: Fixed return value for un/registerChannel. Now returns properly indicate when un/register commands should be sent. + +Build 1.3.2.2: + LexManos: + Setup .gitignore for eclipse bin dirs. + Fixed line endings in install.sh + Added mod_MinecraftForge for simpler logging of minecraft version in crash reports + Added new hooks for connection events, See IConnectionHandler for more details. + Added Packet250CustomPayload handeling and channel registraction management, see MessageManager and IPacketHandler for more details. + Forge now uses unsed fields in C->S Packet1Login to identify itself. None Forge clients will get a graceful disconnect message instead of the 'Unknown packet 230' + diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..e9b9fd5a --- /dev/null +++ b/gradle.properties @@ -0,0 +1,3 @@ +# 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 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..30d399d8 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..e18cba72 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Sep 14 12:28:28 PDT 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +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 100644 index 00000000..91a7e269 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..8a0b282a --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@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 + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/java/electroblob/wizardry/CommonProxy.java b/src/main/java/electroblob/wizardry/CommonProxy.java new file mode 100644 index 00000000..1ae24b85 --- /dev/null +++ b/src/main/java/electroblob/wizardry/CommonProxy.java @@ -0,0 +1,176 @@ +package electroblob.wizardry; + +import electroblob.wizardry.item.ItemSpectralBow; +import electroblob.wizardry.packet.PacketCastContinuousSpell; +import electroblob.wizardry.packet.PacketCastSpell; +import electroblob.wizardry.packet.PacketClairvoyance; +import electroblob.wizardry.packet.PacketGlyphData; +import electroblob.wizardry.packet.PacketNPCCastSpell.Message; +import electroblob.wizardry.packet.PacketPlayerSync; +import electroblob.wizardry.packet.PacketTransportation; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.SoundEvent; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.translation.I18n; +import net.minecraft.world.World; +import net.minecraftforge.common.config.Property; + +/** + * The common proxy for wizardry, serving the usual purpose of dealing with all things that need to be handled + * differently on the client and the server. A lot of the methods here appear to do absolutely nothing; this is + * because they do client-only things which are only handled in the client proxy. + * @see {@link electroblob.wizardry.client.ClientProxy} + * @author Electroblob + * @since Wizardry 1.0 + * */ +@SuppressWarnings("deprecation") +public class CommonProxy { + + // SECTION Registry + // =============================================================================================================== + + public void registerRenderers(){} + + public void initialiseLayers(){} + + public void registerKeyBindings(){} + + public void registerSpellHUD(){} + + public net.minecraft.client.model.ModelBiped getWizardArmourModel(){ return null; } + + public void initMixedFontRenderer(){} + + // SECTION Particles + // =============================================================================================================== + + /** + * Spawns a custom particle of the specified type. + * + * @param type EnumParticleType of the particle + * @param world Reference to the World object + * @param x Particle x position + * @param y Particle y position + * @param z Particle z position + * @param velX Particle x velocity + * @param velY Particle y velocity + * @param velZ Particle z velocity + * @param maxAge Lifetime of the particle in ticks + * @param r Red component of particle colour; will be clamped to between 0 and 1 + * @param g Red component of particle colour; will be clamped to between 0 and 1 + * @param b Red component of particle colour; will be clamped to between 0 and 1 + * @param doGravity Whether the particle is affected by gravity (only affects SPARKLE at the moment) + * @param radius The radius of the particle's motion, for cirular motion particles + */ + public void spawnParticle(WizardryParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge, float r, float g, float b, boolean doGravity, double radius){ + // Does nothing since particles are client-side only + } + + /** Spawns a custom particle of the specified type. doGravity defaults to false and radius defaults to 0. + * Note that some of these settings may not affect the particle; some particles are always affected by + * gravity, for instance. + * + * @param type EnumParticleType of the particle + * @param world Reference to the World object + * @param x Particle x position + * @param y Particle y position + * @param z Particle z position + * @param velX Particle x velocity + * @param velY Particle y velocity + * @param velZ Particle z velocity + * @param maxAge Lifetime of the particle in ticks + * @param r Red component of particle colour; will be clamped to between 0 and 1 (unless this is a MAGIC_FIRE particle, in which case this is the scale) + * @param g Red component of particle colour; will be clamped to between 0 and 1 + * @param b Red component of particle colour; will be clamped to between 0 and 1 + */ + public void spawnParticle(WizardryParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge, float r, float g, float b){ + this.spawnParticle(type, world, x, y, z, velX, velY, velZ, maxAge, r, g, b, false, 0); + } + + /** Spawns a custom particle of the specified type. Colour defaults to white, doGravity defaults to false and radius + * defaults to 0. Note that some of these settings may not affect the particle; some particles are always affected by + * gravity, for instance. + * + * @param type EnumParticleType of the particle + * @param world Reference to the World object + * @param x Particle x position + * @param y Particle y position + * @param z Particle z position + * @param velX Particle x velocity + * @param velY Particle y velocity + * @param velZ Particle z velocity + * @param maxAge Lifetime of the particle in ticks + */ + public void spawnParticle(WizardryParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge){ + this.spawnParticle(type, world, x, y, z, velX, velY, velZ, maxAge, 1, 1, 1, false, 0); + } + + public void spawnTornadoParticle(World world, double x, double y, double z, double velX, double velZ, double radius, int maxAge, IBlockState block, BlockPos pos){} + + // SECTION Items + // =============================================================================================================== + + public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack) { + return null; + } + + /** Returns the translated name of the scroll, taking spell discovery into account. If, for some reason, this gets + * called server-side, it uses the deprecated server version of I18n, with a warning. Since any likely server-side + * use of this method will be for text-based logic purposes (like Bibliocraft's book checking system), and since no + * particular player instance can be accessed, spell discovery is ignored. */ + public String getScrollDisplayName(ItemStack scroll){ + + // I have now learnt that the server side I18n always translates to the default en_US, so I could just return + // a hardcoded name in English instead. + Wizardry.logger.info("A mod has called ItemScroll#getItemStackDisplayName from the server side. Using the" + + "deprecated server-side translation methods as a fallback."); + + // Displays [Empty slot] if spell is continuous. + Spell spell = Spell.get(scroll.getItemDamage()); + if(spell.isContinuous) spell = Spells.none; + + return I18n.translateToLocalFormatted("item.wizardry:wizardry:scroll.name", I18n.translateToLocal("spell." + spell.getUnlocalisedName())).trim(); + } + + public double getConjuredBowDurability(ItemStack stack){ + return ((ItemSpectralBow)WizardryItems.spectral_bow).getDefaultDurabilityForDisplay(stack); + } + + // SECTION Packet Handlers + // =============================================================================================================== + + public void handlePlayerSyncPacket(PacketPlayerSync.Message message){} + public void handleGlyphDataPacket(PacketGlyphData.Message message){} + public void handleCastSpellPacket(PacketCastSpell.Message message){} + public void handleCastContinuousSpellPacket(PacketCastContinuousSpell.Message message){} + public void handleNPCCastSpellPacket(Message message){} + public void handleTransportationPacket(PacketTransportation.Message message){} + public void handleClairvoyancePacket(PacketClairvoyance.Message message){} + + // SECTION Misc + // =============================================================================================================== + + public void setToNumberSliderEntry(Property property){} + //public void setToEntityNameEntry(Property property){} + + /** Plays a sound which moves with the given entity. + * + * @param entity The source of the sound + * @param sound The SoundEvent to play + * @param volume Volume relative to 1 + * @param pitch Pitch relative to 1 + * @param repeat Whether to repeat the sound for as long as the entity is alive (or until stopped manually) + */ + public void playMovingSound(Entity entity, SoundEvent sound, float volume, float pitch, boolean repeat){} + + /** Gets the client side world using Minecraft.getMinecraft().theWorld. Only to be called client side! + * Returns null on the server side. */ + public World getTheWorld(){ return null; } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java new file mode 100644 index 00000000..8d56dce6 --- /dev/null +++ b/src/main/java/electroblob/wizardry/Settings.java @@ -0,0 +1,507 @@ +package electroblob.wizardry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import electroblob.wizardry.packet.PacketSyncSettings; +import electroblob.wizardry.packet.WizardryPacketHandler; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.util.text.translation.I18n; +import net.minecraftforge.common.config.ConfigCategory; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.common.config.Property; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; + +/** Singleton class which deals with everything related to wizardry's config file. To access individual settings, use + * {@link Wizardry#settings}. Also stores a few string constants for easy access. + *

+ * As part of the 1.2 update and code overhaul, the way the config settings work in multiplayer has been tightened up. + * Importantly, there are three different types of config options: + *

+ *

  • Server-only settings. These only affect server-side code and hence are not synced. Changing these locally only + * has an effect if the local game is the host, i.e. a dedicated server, a LAN host or a singleplayer world. Examples + * include worldgen, mob drops and commands. + *
  • Synchronised settings. These settings affect both client-side AND server-side code, and are synchronised with + * each client on login via {@link Settings#sync(EntityPlayerMP)}. Changing these locally + * only has an effect if the local game is the host, i.e. a dedicated server, a LAN host or a singleplayer world. + * Examples include discovery mode and crafting recipes. Note that as far as users are concerned, + * there is no difference in behaviour between server-only and synchronised settings. + *
  • Client-only settings. These settings only affect client-side code and hence are not synced. Each client obeys + * its own values for these, and changing them on a dedicated server will have no effect. These are usually only display + * and controls settings.
  • + *

    + * Each of the settings fields in this class is marked with one of the above categories to indicate which it belongs + * to. This in turn dictates which logical side it should be called from: client, server or both. Do not access a + * config setting from the wrong side, because it may cause unexpected or strange behaviour. + * @since Wizardry 1.2 + * @author Electroblob */ +// NOTE: We could convert over to the @Config system IF it is sufficiently complete in this Forge version for my purposes. +// (For one, I know that the LangKey annotation is not applicable to type declarations in 1.10.2, unlike in 1.11.2) +//@Config(modid = Wizardry.MODID) +@SuppressWarnings("deprecation") // Used server I18n deliberately; we want to write the comments in english. +public final class Settings { + + // Category names + /** The unlocalised name of the spells config category. */ + public static final String SPELLS_CATEGORY = "spells"; + /** The unlocalised name of the resistances config category. */ + public static final String RESISTANCES_CATEGORY = "resistances"; + /** The unlocalised name of the client config category. */ + public static final String CLIENT_CATEGORY = "client"; + /** The unlocalised name of the commands config category. */ + public static final String COMMANDS_CATEGORY = "commands"; + /** The unlocalised name of the worldgen config category. */ + public static final String WORLDGEN_CATEGORY = "worldgen"; + /** The unlocalised name of the global config category. */ + public static final String GLOBAL_CATEGORY = "global"; + + /** The wizardry config file. */ + private Configuration config; + + // Server-only settings. These only affect server-side code and hence are not synced. Changing these locally only + // has an effect if the local game is the host, i.e. a dedicated server, a LAN host or a singleplayer world. + + // Worldgen + /** [Server-only] The rarity of wizard towers, used by the world generator. Larger numbers are rarer. */ + public int towerRarity = 8; + /** [Server-only] List of dimension ids in which to generate crystal ore. */ + public int[] oreDimensions = {0}; + /** [Server-only] List of dimension ids in which to generate crystal ore. */ + public int[] flowerDimensions = {0}; + /** [Server-only] List of dimension ids in which to generate crystal ore. */ + public int[] towerDimensions = {0}; + /** [Server-only] Whether or not wizardry loot should generate in dungeon chests. Note that this does not + * affect the generation of loot in wizard towers. */ + public boolean generateLoot = true; + + // Entities' drops, targeting, damage, etc. + /** [Server-only] Chance (out of 200) for mobs to drop spell books. */ + public int spellBookDropChance = 3; + /** [Server-only] Whether or not players can teleport through unbreakable blocks (e.g. bedrock) using the phase step spell. */ + public boolean teleportThroughUnbreakableBlocks = false; + /** [Server-only] Whether to allow players to damage their designated allies using magic. */ + public boolean friendlyFire = true; + /** [Server-only] Whether to allow players to disarm other players using the telekinesis spell. */ + public boolean telekineticDisarmament = true; + /** [Server-only] Whether summoned creatures can revenge attack their caster if their caster attacks them. */ + public boolean minionRevengeTargeting = true; + /** [Server-only] List of names of entities which summoned creatures are allowed to attack, in addition to the defaults. */ + public String[] summonedCreatureTargetsWhitelist = {}; + /** [Server-only] List of names of entities which summoned creatures are specifically not allowed to attack, overriding the defaults and the whitelist. */ + public String[] summonedCreatureTargetsBlacklist = {"creeper"}; + /** [Server-only] Global damage scaling factor for all player magic damage. */ + public double playerDamageScale = 1.0f; + /** [Server-only] Global damage scaling factor for all npc magic damage. */ + public double npcDamageScale = 1.0f; + + // Commands (these don't need synchronising since typing a command always queries the server). + /** [Server-only] The maximum allowed multiplier for the /cast command. This limit is here to stop people from accidentally + * breaking their worlds! */ + public double maxSpellCommandMultiplier = 20d; + /** [Server-only] The name of the /cast command. */ + public String castCommandName = "cast"; + /** [Server-only] The name of the /discoverspell command. */ + public String discoverspellCommandName = "discoverspell"; + /** [Server-only] The name of the /ally command. */ + public String allyCommandName = "ally"; + /** [Server-only] The name of the /allies command. */ + public String alliesCommandName = "allies"; + + // Synchronised settings. These settings affect both client-side AND server-side code. Changing these locally + // only has an effect if the local game is the host, i.e. a dedicated server, a LAN host or a singleplayer world. + + // Recipes (only need syncing for display in the wizard's handbook) + /** [Synchronised] Whether or not firebombs should be craftable. */ + public boolean firebombIsCraftable = true; + /** [Synchronised] Whether or not poison bombs should be craftable. */ + public boolean poisonBombIsCraftable = true; + /** [Synchronised] Whether or not poison bombs should be craftable. */ + public boolean smokeBombIsCraftable = true; + /** [Synchronised] Whether to require a magic crystal in the blank scroll crafting recipe (in case it conflicts with another mod). */ + public boolean useAlternateScrollRecipe = false; + + // Gamemodes + /** [Synchronised] When set to true, spells a player hasn't cast yet will be unreadable until they are cast (on a per-world basis). Has no effect when in creative mode. Spells of + * identification will be unobtainable in survival mode if this is false. */ + public boolean discoveryMode = true; + + // Client-only settings. These settings only affect client-side code and hence are not synced. Each client obeys + // its own values for these, and changing them on a dedicated server will have no effect. + + // Controls + /** [Client-only] Whether the player can switch between spells on a wand by scrolling with the mouse wheel while + * sneaking. */ + public boolean enableShiftScrolling = true; + + // Display + /** [Client-only] Whether to show summoned creatures' names and owners above their heads. */ + public boolean showSummonedCreatureNames = true; + /** [Client-only] The position of the spell HUD. */ + public GuiPosition spellHUDPosition = GuiPosition.BOTTOM_LEFT; + + /** Set of constants for each of the four positions that the spell HUD can be in. */ + public enum GuiPosition { + + BOTTOM_LEFT("Bottom left"), + TOP_LEFT("Top left"), + TOP_RIGHT("Top right"), + BOTTOM_RIGHT("Bottom right"); + + /** Constant array storing the names of each of the constants, in the order they are declared. */ + public static final String[] names; + + static { + names = new String[values().length]; + for(GuiPosition position : values()){ + names[position.ordinal()] = position.name; + } + } + + /** The readable name for this GUI position that will be displayed on the button in the config GUI. */ + public final String name; + + GuiPosition(String name){ + this.name = name; + } + + /** Gets a GUI position from its string name (ignoring case), or BOTTOM_LEFT if the given name is not a valid position. */ + public static GuiPosition fromName(String name){ + + for(GuiPosition position : values()){ + if(position.name.equalsIgnoreCase(name)) return position; + } + + Wizardry.logger.info("Invalid string for the spell HUD position. Using default (bottom left) instead."); + return BOTTOM_LEFT; + } + + } + + // As of Wizardry 1.1, all keys used in the config file itself are now hardcoded, not localised. The localisations + // are done in the config GUI. The config file has to be written in one language or it will end up with multiple + // options for different languages. + + // These methods are package-protected to stop anyone else from calling them. + + /** Called from preInit to initialise the config file. The first part of the config file has to be done here (as is + * conventional) so that the various registries can change what they do accordingly. The spell part of the config + * has to be done in the init method.*/ + void initConfig(FMLPreInitializationEvent event){ + + config = new Configuration(event.getSuggestedConfigurationFile()); + config.load(); + + Wizardry.logger.info("Setting up main config"); + + setupGeneralConfig(); + + config.save(); + } + + /** Called from init to initialise the parts of the config file that depend on other mods. For example, the spell + * config is done here and not in preInit because all spells must be registered before it is added, including + * those in other mods. */ + void initConfigExtras(){ + + Wizardry.logger.info("Setting up spells config for " + Spell.getTotalSpellCount() + " spells"); + + setupSpellsConfig(); + + config.save(); + } + + /** Called to save changes to the config file after it has been edited in game from the menus. */ + void saveConfigChanges(){ + + Wizardry.logger.info("Saving in-game config changes"); + + setupGeneralConfig(); + setupSpellsConfig(); + + config.save(); + } + + /** Sends a packet to the specified player's client containing all the synchronised settings. */ + public void sync(EntityPlayerMP player){ + Wizardry.logger.info("Synchronising config settings for " + player.getName()); + IMessage message = new PacketSyncSettings.Message(this); + WizardryPacketHandler.net.sendTo(message, player); + } + + public ConfigCategory getConfigCategory(String name){ + return config.getCategory(name); + } + + private void setupSpellsConfig(){ + + config.addCustomCategoryComment(SPELLS_CATEGORY, "Set a spell to false to disable it. Disabled spells will still have their associated spell book (mainly so the spell books don't all change) and can still be bound to wands, but cannot be cast in game, will not appear in any subsequently generated chests or wizard trades and will not drop from mobs. Disable a spell if it is causing problems, conflicts with another mod or creates an unintended exploit."); + + Property property; + + for(Spell spell : Spell.getSpells(Spell.allSpells)){ + property = config.get(SPELLS_CATEGORY, spell.getRegistryName().toString(), true, + I18n.translateToLocal("spell." + spell.getUnlocalisedName() + ".desc")); + // Uses the same config key as the spell name, because - well, that's what it's called! + property.setLanguageKey("spell." + spell.getUnlocalisedName()); + spell.setEnabled(property.getBoolean()); + } + + } + + private void setupGeneralConfig(){ + + // This trick is borrowed from forge; it sorts the config options into the order you want them. + List propOrder = new ArrayList(); + + Property property; + + config.addCustomCategoryComment(Configuration.CATEGORY_GENERAL, "Please note that changing some of these settings may make the mod very difficult to play."); + + property = config.get(Configuration.CATEGORY_GENERAL, "towerRarity", 8, "Rarity of wizard towers. Higher numbers are rarer. Set to 0 to disable wizard towers completely.", 0, 50); + property.setLanguageKey("config.wizardry.tower_rarity"); + property.setRequiresWorldRestart(true); + Wizardry.proxy.setToNumberSliderEntry(property); + towerRarity = property.getInt(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "spellBookDropChance", 3, "The chance for mobs to drop a spell book when killed. The greater this number, the more often they will drop. Set to 0 to disable spell book drops. Set to 200 for guaranteed drops.", 0, 200); + property.setLanguageKey("config.wizardry.spell_book_drop_chance"); + Wizardry.proxy.setToNumberSliderEntry(property); + spellBookDropChance = property.getInt(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "oreDimensions", new int[]{0}, "List of dimension ids in which crystal ore will generate. Note that removing the overworld (id 0) from this list will make the mod VERY difficult to play!"); + property.setLanguageKey("config.wizardry.ore_dimensions"); + property.setRequiresWorldRestart(true); + oreDimensions = property.getIntList(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "flowerDimensions", new int[]{0}, "List of dimension ids in which crystal flowers will generate."); + property.setLanguageKey("config.wizardry.flower_dimensions"); + property.setRequiresWorldRestart(true); + flowerDimensions = property.getIntList(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "towerDimensions", new int[]{0}, "List of dimension ids in which wizard towers will generate."); + property.setLanguageKey("config.wizardry.tower_dimensions"); + property.setRequiresWorldRestart(true); + towerDimensions = property.getIntList(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "generateLoot", true, "Whether to generate wizardry loot in dungeon chests."); + property.setLanguageKey("config.wizardry.generate_loot"); + property.setRequiresWorldRestart(true); + generateLoot = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "firebombIsCraftable", true, "Whether firebombs can be crafted or not."); + property.setLanguageKey("config.wizardry.firebomb_is_craftable"); + property.setRequiresMcRestart(true); + firebombIsCraftable = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "poisonBombIsCraftable", true, "Whether poison bombs can be crafted or not."); + property.setLanguageKey("config.wizardry.poison_bomb_is_craftable"); + property.setRequiresMcRestart(true); + poisonBombIsCraftable = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "smokeBombIsCraftable", true, "Whether smoke bombs can be crafted or not."); + property.setLanguageKey("config.wizardry.smoke_bomb_is_craftable"); + property.setRequiresMcRestart(true); + smokeBombIsCraftable = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "useAlternateScrollRecipe", false, "Whether to require a magic crystal in the shapeless crafting recipe for blank scrolls. Set to true if another mod adds a conflicting recipe."); + property.setLanguageKey("config.wizardry.use_alternate_scroll_recipe"); + property.setRequiresMcRestart(true); + useAlternateScrollRecipe = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "teleportThroughUnbreakableBlocks", false, "Whether players are allowed to teleport through unbreakable blocks (e.g. bedrock) using the phase step spell."); + property.setLanguageKey("config.wizardry.teleport_through_unbreakable_blocks"); + teleportThroughUnbreakableBlocks = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "showSummonedCreatureNames", true, "Whether to show summoned creatures' names and owners above their heads."); + property.setLanguageKey("config.wizardry.show_summoned_creature_names"); + showSummonedCreatureNames = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "friendlyFire", true, "Whether to allow players to damage their designated allies using magic."); + property.setLanguageKey("config.wizardry.friendly_fire"); + friendlyFire = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "telekineticDisarmament", true, "Whether to allow players to disarm other players using the telekinesis spell. Set to false to prevent stealing of items."); + property.setLanguageKey("config.wizardry.telekinetic_disarmament"); + telekineticDisarmament = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "discoveryMode", true, "For those who like a sense of mystery! When set to true, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is false."); + property.setLanguageKey("config.wizardry.discovery_mode"); + property.setRequiresWorldRestart(true); + discoveryMode = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "enableShiftScrolling", true, "Whether you can switch between spells on a wand by scrolling with the mouse wheel while sneaking. Note that this will only affect you; other players connected to the same server obey their own settings."); + property.setLanguageKey("config.wizardry.enable_shift_scrolling"); + property.setRequiresWorldRestart(false); + enableShiftScrolling = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "minionRevengeTargeting", true, "Whether summoned creatures can revenge attack their owner if their owner attacks them."); + property.setLanguageKey("config.wizardry.minion_revenge_targeting"); + property.setRequiresWorldRestart(false); + minionRevengeTargeting = property.getBoolean(); + propOrder.add(property.getName()); + + // These two aren't sliders because using a slider makes it difficult to fine-tune the numbers; the nature of a + // scaling factor means that 0.5 is as big a change as 2.0, so whilst a slider is fine for increasing the damage, + // it doesn't give fine enough control for values less than 1. + property = config.get(Configuration.CATEGORY_GENERAL, "playerDamageScaling", 1.0, "Global damage scaling factor for the damage dealt by players casting spells, relative to 1.", 0, 20); + property.setLanguageKey("config.wizardry.player_damage_scaling"); + playerDamageScale = property.getDouble(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "npcDamageScaling", 1.0, "Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1.", 0, 20); + property.setLanguageKey("config.wizardry.npc_damage_scaling"); + npcDamageScale = property.getDouble(); + propOrder.add(property.getName()); + + // This one isn't a slider either because people are likely to want exact values ("it must be at most 50.3" is a bit strange!). + property = config.get(Configuration.CATEGORY_GENERAL, "castCommandMultiplierLimit", 20.0, "Upper limit for the multipliers passed into the /cast command. This is here to stop players from accidentally breaking a world/server. Large blast mutipliers can cause extreme lag - you have been warned!", 1, 255); + property.setLanguageKey("config.wizardry.cast_command_multiplier_limit"); + maxSpellCommandMultiplier = property.getDouble(); + propOrder.add(property.getName()); + + property = config.get(Configuration.CATEGORY_GENERAL, "summonedCreatureTargetsWhitelist", new String[0], "List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard)."); + property.setLanguageKey("config.wizardry.summoned_creature_targets_whitelist"); + property.setRequiresWorldRestart(true); + //Wizardry.proxy.setToEntityNameEntry(property); + summonedCreatureTargetsWhitelist = property.getStringList(); + // Converts all strings in the list to lower case, to ignore case sensitivity, and trims them. + for(int i=0; i propOrder1 = new ArrayList(); + + config.addCustomCategoryComment(RESISTANCES_CATEGORY, "Settings which allow entities to be made immune to certain types of magic. In multiplayer, the server/LAN host settings will apply."); + + property = config.get(RESISTANCES_CATEGORY, "mobsImmuneToFire", new String[]{}, "List of names of entities that are immune to fire, in addition to the defaults. Add mod creatures to this list if you want them to be immune to fire magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard)."); + property.setLanguageKey("config.wizardry.mobs_immune_to_fire"); + property.setRequiresMcRestart(true); + //Wizardry.proxy.setToEntityNameEntry(property); + // Converts all strings in the list to lower case, to ignore case sensitivity, and trims them. + for(int i=0; i randomNames = new HashMap(Spell.getTotalSpellCount()); + public Map randomDescriptions = new HashMap(Spell.getTotalSpellCount()); + + // Required constructors + public SpellGlyphData() { + this(NAME); + } + + public SpellGlyphData(String name){ + super(name); + } + + /** Generates random names and descriptions for any spells which don't already have them. */ + public void generateGlyphNames(World world){ + + for(Spell spell : Spell.getSpells(Spell.allSpells)){ + if(!randomNames.containsKey(spell)) randomNames.put(spell, generateRandomName(world.rand)); + } + + for(Spell spell : Spell.getSpells(Spell.allSpells)){ + if(!randomDescriptions.containsKey(spell)) randomDescriptions.put(spell, generateRandomDescription(world.rand)); + } + + this.markDirty(); + } + + private String generateRandomName(Random random){ + + String name = ""; + + for(int i=0; i names = new ArrayList(); + List descriptions = new ArrayList(); + + for(Spell spell : Spell.getSpells(Spell.allSpells)){ + names.add(this.randomNames.get(spell)); + descriptions.add(this.randomDescriptions.get(spell)); + } + + PacketGlyphData.Message msg = new PacketGlyphData.Message(names, descriptions); + + WizardryPacketHandler.net.sendTo(msg, player); + + } + + /** Helper method to retrieve the random glyph name for the given spell from the map stored in the given world. */ + public static String getGlyphName(Spell spell, World world){ + Map names = SpellGlyphData.get(world).randomNames; + return names == null ? "" : names.get(spell); + } + + /** Helper method to retrieve the random glyph description for the given spell from the map stored in the given world. */ + public static String getGlyphDescription(Spell spell, World world){ + Map descriptions = SpellGlyphData.get(world).randomDescriptions; + return descriptions == null ? "" : descriptions.get(spell); + } + + @Override + public void readFromNBT(NBTTagCompound nbt){ + + this.randomNames = new HashMap(); + this.randomDescriptions = new HashMap(); + + NBTTagList tagList = nbt.getTagList("spellGlyphData", NBT.TAG_COMPOUND); + + for(int i=0; i + * Forge seems to have separate classes to hold the Capability<...> instance ('key') and + * methods for getting the capability, but in my opinion there are already too many classes to deal with, so I'm not + * adding any more than are necessary, meaning those constants and values are kept here instead. + * @since Wizardry 1.2 + * @author Electroblob */ +// On the plus side, having to rethink this class allowed me to clean it up a lot. +public class WizardData implements INBTSerializable { + + /** Static instance of what I like to refer to as the capability key. Private because, well, it's internal! */ + // This annotation does some crazy Forge magic behind the scenes and assigns this field a value. + @CapabilityInject(WizardData.class) + private static final Capability WIZARD_DATA_CAPABILITY = null; + + private final EntityPlayer player; + + // This one is still necessary, because I can't override the equip animation for items that aren't from Wizardry. + private Map imbuementDurations; + + public boolean hasSpiritWolf; + public boolean hasSpiritHorse; + + /** Whether this player is currently casting a continuous spell via commands. Not saved over world reload + * and reset on player death. */ + private Spell currentlyCasting; + /** The time for which this player has been casting a continuous spell via commands. Increments by 1 each tick. + * Not saved over world reload and reset on player death. */ + private int castingTick; + /** SpellModifiers object for the current continuous spell cast via commands. Not saved over world reload and + * reset on player death. */ + private SpellModifiers spellModifiers; + /** Coordinates for the saved transportation stone circle location. Will be null if no location is saved. */ + private BlockPos stoneCircleLocation; + /** Dimension id which the saved stone circle is in. */ + private int stoneCircleDimension; + /** Time left until the player teleports under the effect of transportation */ + private int tpCountdown; + + /** Coordinates for the saved clairvoyance location. Will be null if no location is saved. */ + private BlockPos clairvoyanceLocation; + /** Dimension id which the saved clairvoyance point is in. */ + private int clairvoyanceDimension; + + public EntityShield shield; + + public WeakReference selectedMinion; + + /** Set of this player's discovered spells. Do not write to this list directly, use + * {@link WizardData#discoverSpell(Spell)} instead. */ + public Set spellsDiscovered; + + private Set allies; + /** List of usernames of this player's allies. May not be accurate 100% of the time. This is here so that a player + * can view the usernames of their allies even when those allies are not online. + * Do not use this for any other purpose than displaying the names! */ + public Set allyNames; + + private Set soulboundCreatures; + + public WizardData(EntityPlayer player){ + this.player = player; + this.imbuementDurations = new HashMap(); + this.spellsDiscovered = new HashSet(); + // All players can recognise magic missile. This is not done using discoverSpell because that seems to cause + // a crash on load occasionally (probably something to do with achievements being initalised) + this.spellsDiscovered.add(Spells.magic_missile); + this.hasSpiritWolf = false; + this.hasSpiritHorse = false; + this.currentlyCasting = Spells.none; + this.spellModifiers = new SpellModifiers(); + this.castingTick = 0; + this.stoneCircleDimension = 0; + this.clairvoyanceDimension = 0; + this.setTpCountdown(0); + this.allies = new HashSet(); + this.allyNames = new HashSet(); + this.soulboundCreatures = new HashSet(); + } + + public boolean hasSpellBeenDiscovered(Spell spell){ + return spellsDiscovered.contains(spell) || spell instanceof None; + } + + /** + * Adds the given spell to the list of discovered spells for this player. Automatically takes into account + * whether the spell has been discovered. Use this method rather than adding directly to the list because it + * handles achievements. + * @param spell The spell to be discovered + * @return True if the spell had not already been discovered; false otherwise. + */ + public boolean discoverSpell(Spell spell){ + + if(spellsDiscovered == null){ + spellsDiscovered = new HashSet(); + } + // The 'none' spell cannot be discovered + if(spell instanceof None) return false; + // Tries to add the spell to the list of discovered spells, and returns false if it was already present + if(!spellsDiscovered.add(spell)) return false; + // If the spell had not already been discovered, achievements can be triggered and the method returns true + if(spellsDiscovered.containsAll(Spell.getSpells(Spell::isEnabled))){ + this.player.addStat(WizardryAchievements.all_spells); + } + + for(Element element : Element.values()){ + if(element != Element.MAGIC && spellsDiscovered.containsAll(Spell.getSpells(new Spell.TierElementFilter(null, element)))){ + this.player.addStat(WizardryAchievements.element_master); + } + } + + return true; + } + + /** Sets the player's saved transportation stone location and dimension. */ + public void setStoneCircleLocation(BlockPos pos, int dimensionID){ + this.stoneCircleLocation = pos; + this.stoneCircleDimension = dimensionID; + } + + /** Returns the coordinates of the associated player's saved transportation stone circle. */ + public BlockPos getStoneCircleLocation(){ return stoneCircleLocation; } + /** Returns the dimension ID of the associated player's saved transportation stone circle. */ + public int getStoneCircleDimension(){ return stoneCircleDimension; } + + public int getTpCountdown() { return tpCountdown; } + + public void setTpCountdown(int tpCountdown) { this.tpCountdown = tpCountdown; } + + /** Sets the player's saved clairvoyance location. */ + public void setClairvoyancePoint(BlockPos pos, int dimensionID){ + this.clairvoyanceLocation = pos; + this.clairvoyanceDimension = dimensionID; + } + + /** Returns the coordinates for the saved clairvoyance location. Will be null if no location is saved. */ + public BlockPos getClairvoyanceLocation(){ return clairvoyanceLocation; } + + /** Returns the dimension ID for the saved clairvoyance location. Will be null if no location is saved. */ + public int getClairvoyanceDimension(){ return clairvoyanceDimension; } + + /** Overwrites the imbuement duration associated with the given imubement for this player, or creates it if there + * was none previously. + * @throws IllegalArgumentException if the given {@link Enchantment} is not an {@link Imbuement}. */ + public void setImbuementDuration(Enchantment enchantment, int duration){ + // It is best to throw an exception here, because otherwise the error would either go unnoticed (if non-imbuements + // were ignored) or cause a ClassCastException later (if non-imbuements were allowed to be added). + if(enchantment instanceof Imbuement){ + this.imbuementDurations.put((Imbuement) enchantment, duration); + }else{ + throw new IllegalArgumentException("Attempted to set an imbuement duration for something that isn't an Imbuement! (This exception has been thrown now to prevent a ClassCastException from occurring later.)"); + } + } + + /** Returns the imbuement duration associated with the given imbuement for this player, or 0 if it does not exist. */ + public int getImbuementDuration(Enchantment enchantment){ + // Need to check that i is not null, otherwise it throws an NPE when Java auto-unboxes it. + // What's nice here is that the map simply accepts objects as keys, so there's no need to cast or throw exceptions. + Integer i = this.imbuementDurations.get(enchantment); + // If i is null, returns 0; otherwise returns i, auto-unboxed to an int. + return i == null ? 0 : i; + } + + /** Decrements the duration for each conjured item by 1, and removes from the map any that are 0 or less or that + * the player no longer has. Also deletes the item from the player's inventory if it runs out of time. */ + private void updateImbuedItems(){ + + Set activeImbuements = new HashSet(); + + // For each item in the player's inventory + for(ItemStack stack : player.inventory.mainInventory){ + if(stack != null && stack.isItemEnchanted()){ + + Map enchantments = EnchantmentHelper.getEnchantments(stack); + + Iterator> iterator = enchantments.entrySet().iterator(); + // For each of the item's enchantments + while(iterator.hasNext()){ + Enchantment enchantment = iterator.next().getKey(); + // Ignores the enchantment unless it is an imbuement + if(enchantment instanceof Imbuement){ + int duration = this.getImbuementDuration(enchantment); + // If the imbuement is still active: + if(duration > 0){ + // Decrements the timer + this.imbuementDurations.put((Imbuement)enchantment, duration-1); + // Adds this imbuement to the set of imbuements that need to be kept + activeImbuements.add((Imbuement)enchantment); + // Otherwise: + }else{ + // Removes the enchantment from the enchantment map + iterator.remove(); + // Applies the new enchantment map to the item + EnchantmentHelper.setEnchantments(enchantments, stack); + } + } + } + } + } + // Removes all imbuements from the map that are no longer active + this.imbuementDurations.keySet().retainAll(activeImbuements); + } + + /** Adds the given player to the list of allies belonging to the associated player, or removes the player if + * they are already in the list of allies. Returns true if the player was added, false if they were removed. */ + public boolean toggleAlly(EntityPlayer player){ + if(this.isPlayerAlly(player)){ + this.allies.remove(player.getUniqueID()); + // The remove method uses .equals() rather than == so this will work fine. + this.allyNames.remove(player.getName()); + return false; + }else{ + this.allies.add(player.getUniqueID()); + this.allyNames.add(player.getName()); + return true; + } + } + + /** Returns whether the given player is in this player's list of allies, or is on the same team as this player. */ + public boolean isPlayerAlly(EntityPlayer player){ + return this.allies.contains(player.getUniqueID()) || this.player.isOnSameTeam(player); + } + + /** Adds the given entity to this player's list of soulbound creatures, and returns whether it succeeded. */ + public boolean soulbind(EntityLivingBase target){ + return this.soulboundCreatures.add(target.getUniqueID()); + } + + /** Returns whether the given entity has been soulbound to this player. */ + public boolean isCreatureSoulbound(EntityPlayer target){ + return this.soulboundCreatures.contains(target.getUniqueID()); + } + + /** Damages all creatures soulbound to this player by the given amount, and removes from the list any that no + * longer exist. */ + public void damageAllSoulboundCreatures(float damage){ + + for(Iterator iterator = this.soulboundCreatures.iterator(); iterator.hasNext();){ + + Entity entity = WizardryUtilities.getEntityByUUID(this.player.worldObj, iterator.next()); + + if(entity == null) iterator.remove(); + + if(entity instanceof EntityLivingBase){ + // Retaliatory effect + if(entity.attackEntityFrom(MagicDamage.causeDirectMagicDamage(this.player, DamageType.MAGIC, true), damage)){ + // Sound only plays if the damage succeeds + player.playSound(SoundEvents.ENTITY_WITHER_HURT, 1.0F, player.worldObj.rand.nextFloat() * 0.2F + 1.0F); + } + } + } + } + + /** Starts casting the given spell with the given modifiers. */ + public void startCastingContinuousSpell(Spell spell, SpellModifiers modifiers){ + + this.currentlyCasting = spell; + this.spellModifiers = modifiers; + + if(!this.player.worldObj.isRemote){ + PacketCastContinuousSpell.Message message = new PacketCastContinuousSpell.Message(this.player.getEntityId(), + spell.id(), this.spellModifiers); + WizardryPacketHandler.net.sendToDimension(message, this.player.worldObj.provider.getDimension()); + } + } + + /** Stops casting the current spell. */ + public void stopCastingContinuousSpell(){ + + this.currentlyCasting = Spells.none; + this.castingTick = 0; + this.spellModifiers.reset(); + + if(!this.player.worldObj.isRemote){ + PacketCastContinuousSpell.Message message = new PacketCastContinuousSpell.Message(this.player.getEntityId(), + Spells.none.id(), this.spellModifiers); + WizardryPacketHandler.net.sendToDimension(message, this.player.worldObj.provider.getDimension()); + } + } + + /** Returns whether this player is currently casting a continuous spell via commands. */ + public boolean isCasting(){ + return this.currentlyCasting != null && this.currentlyCasting != Spells.none; + } + + /** Returns the continuous spell this player is currently casting via commands, or the 'none' spell if they aren't + * casting anything. */ + public Spell currentlyCasting(){ + return currentlyCasting; + } + + /** Called from the event handler each time the associated player is updated. */ + public void update(){ + + if(this.selectedMinion != null && this.selectedMinion.get() == null) this.selectedMinion = null; + + // This new system removes a lot of repetitive event handler code and inflexible variables which had duplicate + // functions, just for different enchantments. + updateImbuedItems(); + + if(!player.worldObj.isRemote){ + if(getTpCountdown() == 1){ + player.setPositionAndUpdate(this.stoneCircleLocation.getX() + 0.5, this.stoneCircleLocation.getY(), + this.stoneCircleLocation.getZ() + 0.5); + player.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, 50, 0)); + IMessage msg = new PacketTransportation.Message(player.getEntityId()); + WizardryPacketHandler.net.sendToDimension(msg, player.worldObj.provider.getDimension()); + } + + if(getTpCountdown() > 0){ + setTpCountdown(getTpCountdown() - 1); + } + } + + if(this.currentlyCasting != null && this.currentlyCasting.isContinuous){ + this.currentlyCasting.cast(player.worldObj, player, EnumHand.MAIN_HAND, castingTick++, this.spellModifiers); + }else{ + this.castingTick = 0; + } + } + + /** + * Returns the WizardData instance for the specified player. + */ + public static final WizardData get(EntityPlayer player){ + return player.getCapability(WIZARD_DATA_CAPABILITY, null); + } + + /** Called from the event handler each time the associated player entity is cloned, i.e. on respawn or when + * travelling to a different dimension. Used to copy over any variables that should persist over player death. + * This is the inverse of the old onPlayerDeath method, which reset the variables that shouldn't persist. + * @param data The old WizardData whose variables are to be copied over. + * @param respawn True if the player died and is respawning, false if they are just travelling between dimensions. */ + public void copyFrom(WizardData data, boolean respawn){ + // TODO: What happens with spirit wolf and spirit horse? + this.hasSpiritHorse = data.hasSpiritHorse; + this.hasSpiritWolf = data.hasSpiritWolf; + this.allies = data.allies; + this.allyNames = data.allyNames; + this.clairvoyanceDimension = data.clairvoyanceDimension; + this.clairvoyanceLocation = data.clairvoyanceLocation; + this.selectedMinion = data.selectedMinion; + // Curse of soulbinding is lifted when the caster dies, but not when they switch dimensions. + if(!respawn) this.soulboundCreatures = data.soulboundCreatures; + this.spellsDiscovered = data.spellsDiscovered; + this.stoneCircleDimension = data.stoneCircleDimension; + this.stoneCircleLocation = data.stoneCircleLocation; + + // Imbuements are lost on death so their durations do not persist. + // Command spell casting is reset on death so the associated variables do not persist. + // tpCountdown is reset both when the player dies and when they switch dimensions. + + } + + /** Sends a packet to this player's client to synchronise necessary information. Only called server side. */ + public void sync(){ + if(this.player instanceof EntityPlayerMP){ + int id = -1; + if(this.selectedMinion != null && this.selectedMinion.get() instanceof Entity) id = ((Entity)this.selectedMinion.get()).getEntityId(); + IMessage msg = new PacketPlayerSync.Message(this.spellsDiscovered, id); + WizardryPacketHandler.net.sendTo(msg, (EntityPlayerMP)this.player); + } + } + + @Override + public NBTTagCompound serializeNBT() { + + NBTTagCompound properties = new NBTTagCompound(); + + // ...so Java 8 allows you to do stuff like this: + properties.setTag("imbuements", WizardryUtilities.mapToNBT(this.imbuementDurations, + imbuement -> new NBTTagInt(Enchantment.getEnchantmentID((Enchantment)imbuement)), NBTTagInt::new)); + + properties.setBoolean("hasSpiritWolf", this.hasSpiritWolf); + properties.setBoolean("hasSpiritHorse", this.hasSpiritHorse); + + if(this.stoneCircleLocation != null) properties.setLong("stoneCircleLocation", this.stoneCircleLocation.toLong()); + properties.setInteger("stoneCircleDimension", this.stoneCircleDimension); + properties.setInteger("tpCountdown", this.tpCountdown); + + if(this.clairvoyanceLocation != null) properties.setLong("clairvoyanceLocation", this.clairvoyanceLocation.toLong()); + properties.setInteger("clairvoyanceDimension", this.getClairvoyanceDimension()); + + // THIS is why I wrote the list/map <-> NBT methods. Look how neat this is! + properties.setTag("allies", WizardryUtilities.listToNBT(this.allies, WizardryUtilities::UUIDtoTagCompound)); + properties.setTag("allyNames", WizardryUtilities.listToNBT(this.allyNames, NBTTagString::new)); + properties.setTag("soulboundCreatures", WizardryUtilities.listToNBT(this.soulboundCreatures, WizardryUtilities::UUIDtoTagCompound)); + + // Might be worth converting this over to WizardryUtilities.listToNBT. + int[] spells = new int[this.spellsDiscovered.size()]; + int i=0; + for(Spell spell : this.spellsDiscovered){ + spells[i] = spell.id(); + i++; + } + properties.setIntArray("discoveredSpells", spells); + + return properties; + } + + @Override + public void deserializeNBT(NBTTagCompound nbt) { + + if(nbt != null){ + + this.imbuementDurations = WizardryUtilities.NBTToMap(nbt.getTagList("imbuements", NBT.TAG_COMPOUND), + (NBTTagInt tag) -> (Imbuement)Enchantment.getEnchantmentByID(tag.getInt()), NBTTagInt::getInt); + + this.hasSpiritWolf = nbt.getBoolean("hasSpiritWolf"); + this.hasSpiritHorse = nbt.getBoolean("hasSpiritHorse"); + + this.stoneCircleLocation = BlockPos.fromLong(nbt.getLong("stoneCircleLocation")); + this.stoneCircleDimension = nbt.getInteger("stoneCircleDimension"); + this.tpCountdown = nbt.getInteger("tpCountdown"); + + this.clairvoyanceLocation = BlockPos.fromLong(nbt.getLong("clairvoyanceLocation")); + this.clairvoyanceDimension = nbt.getInteger("clairvoyanceDimension"); + + this.allies = new HashSet(WizardryUtilities.NBTToList(nbt.getTagList("allies", NBT.TAG_COMPOUND), + WizardryUtilities::tagCompoundToUUID)); + + this.allyNames = new HashSet(WizardryUtilities.NBTToList(nbt.getTagList("allyNames", NBT.TAG_STRING), + NBTTagString::getString)); + + this.soulboundCreatures = new HashSet(WizardryUtilities.NBTToList(nbt.getTagList("soulboundCreatures", NBT.TAG_COMPOUND), + WizardryUtilities::tagCompoundToUUID)); + + this.spellsDiscovered = new HashSet(); + for(int id : nbt.getIntArray("discoveredSpells")){ + spellsDiscovered.add(Spell.get(id)); + } + } + } + + /** This is a nested class for a few reasons: firstly, it makes sense because instances of this and WizardData go + * hand-in-hand; secondly, it's too short to be worth a separate file; and thirdly (and most importantly) it allows + * me to access WIZARD_DATA_CAPABILITY while keeping it private. */ + public static class Provider implements ICapabilitySerializable { + + private final WizardData data; + + public Provider(EntityPlayer player){ + data = new WizardData(player); + } + + @Override + public boolean hasCapability(Capability capability, EnumFacing facing) { + return capability == WIZARD_DATA_CAPABILITY; + } + + @Override + public T getCapability(Capability capability, EnumFacing facing) { + + if(capability == WIZARD_DATA_CAPABILITY){ + return WIZARD_DATA_CAPABILITY.cast(data); + } + + return null; + } + + @Override + public NBTTagCompound serializeNBT() { + return data.serializeNBT(); + } + + @Override + public void deserializeNBT(NBTTagCompound nbt) { + data.deserializeNBT(nbt); + } + + } + + // Ended up deleting IWizardData because it was unnecessary. This is the comment that was at the start of it: + + /* I'm not going to lie, I will never find the capabilities system even remotely intuitive so this is a bare-minimum + * approach just to get things working (four classes where one would have done?!) At one point I considered simply + * wrapping my old IEEP inside a single-field capability, but I eventually decided I would at least *try* to do it + * properly. + * + * "...without having to directly implement many interfaces." - Forge Docs. I still can't see what's wrong with + * implementing many interfaces; surely that's what Java interfaces are designed for? + * + * Other things I find annoying: + * - IStorage. It's completely redundant in the majority of cases, and I don't understand why we need yet another + * separate class. + * - Making an interface, only to implement it once and once only. This completely defeats the point of interfaces. + * - The EnumFacing parameter, which is again redundant for everything that isn't a tile entity. So much for a clean, + * neat system. + * + * What Forge has effectively done is conflated two different functions: attaching data to stuff and cross-mod + * integration/soft dependencies. I think this is bad design; it would have been better to keep the two features separate. + * + * Here's my current understanding of how the capability system works: + * - You make an interface which defines the things your capability can do (this class). I will call this the TEMPLATE. + * - You implement that interface with your default implementation (WizardData). This is the closest analog to your old + * IEEP implementation class. THIS CLASS STORES ALL THE VARIABLES, and hence has one instance for each instance of whatever + * it is attached to. I will call this the DATA. + * - The DATA class implements INBTSerializable (assuming you want it to be saved, which is nearly always the case) + * - Despite its name, Capability does NOT represent a capability itself. Instead, it acts as a sort of identifier/key, + * the idea being that you can access a particular instance of your DATA given the key (which tells forge that you want a + * capability of type TEMPLATE) and the object you want the DATA for. This is what Entity.getCapability(...) does. + * + * To really understand what's going on though, you need to sift through Forge's verbose data structures and find where + * capabilities are actually hooked into vanilla: + * - Anything that implements ICapabilityProvider will have a private CapabilityDispatcher field. This holds other + * ICapabilityProviders. (I know. This inheritance pattern DOES NOT MAKE SENSE, because these could, in theory, be OTHER + * ENTITIES!) + * - This field is assigned a value through Forge's event factory, which, as we are all familiar with, calls all the + * methods marked with @SubscribeEvent. These methods add individual ICapabilityProviders to a Map stored in the event, + * which the event factory then wraps in a CapabilityDispatcher (which is itself an ICapabilityProvider) for the object + * that called it. + * - In your event handler, you return a custom ICapabilityProvider which is effectively bolted on to the player, and + * duplicates the ICapabilityProvider methods so you can hook into them and return an instance of your DATA class. + * - Where before there was a simple collection of IEEPs stored in the player, there is now a tree of ICapabilityProviders: + * + * - Entity/TileEntity/ItemStack + * - Vanilla ICapabilityProviders, mostly IItemHandlers, stored as fields. + * - CapabilityDispatcher, stored as a field. + * - Custom ICapabilityProviders + * - Custom CapabilityDispatchers + * - ... + * + * Most importantly, EACH PLAYER HOLDS THEIR OWN INSTANCE OF THIS TREE. + * + * When a capability is retrieved, the following process happens: + * 1. For the Entity/TileEntity/ItemStack instance, ICapabilityProvider.getCapability(...) is called. + * 2. The request propogates through the tree and finds the requested capability. */ + +} diff --git a/src/main/java/electroblob/wizardry/Wizardry.java b/src/main/java/electroblob/wizardry/Wizardry.java new file mode 100644 index 00000000..9e30a715 --- /dev/null +++ b/src/main/java/electroblob/wizardry/Wizardry.java @@ -0,0 +1,265 @@ +package electroblob.wizardry; + +import org.apache.logging.log4j.Logger; + +import electroblob.wizardry.command.CommandCastSpell; +import electroblob.wizardry.command.CommandDiscoverSpell; +import electroblob.wizardry.command.CommandSetAlly; +import electroblob.wizardry.command.CommandViewAllies; +import electroblob.wizardry.packet.WizardryPacketHandler; +import electroblob.wizardry.registry.WizardryAchievements; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryRegistry; +import electroblob.wizardry.registry.WizardryTabs; +import net.minecraft.item.Item; +import net.minecraft.nbt.NBTBase; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.AchievementPage; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.Capability.IStorage; +import net.minecraftforge.common.capabilities.CapabilityManager; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventHandler; +import net.minecraftforge.fml.common.Mod.Instance; +import net.minecraftforge.fml.common.SidedProxy; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLMissingMappingsEvent; +import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.event.FMLServerStartingEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.network.NetworkRegistry; +import net.minecraftforge.fml.common.registry.GameRegistry; +import net.minecraftforge.fml.common.registry.GameRegistry.Type; + +@Mod(modid = Wizardry.MODID, name = Wizardry.NAME, version = Wizardry.VERSION, guiFactory = "electroblob." + Wizardry.MODID + ".WizardryGuiFactory") +public class Wizardry { + + /** Wizardry's mod ID. */ + public static final String MODID = "wizardry"; + /** Wizardry's mod name, in readable form. */ + public static final String NAME = "Electroblob's Wizardry"; + /** The version number for this version of wizardry. The following system is used for version numbers: + *

    [major Minecraft version].[major mod version].[minor mod version/patch]

    + * The major mod version is consistent across Minecraft versions, i.e. Wizardry 1.1 has the same features as + * Wizardry 2.1, but they are for different versions of Minecraft and have separate minor versioning. 1.x.x + * represents Minecraft 1.7.x versions, 2.x.x represents Minecraft 1.10.x versions, 3.x.x represents Minecraft + * 1.11.x versions, and so on. */ + public static final String VERSION = "2.1.0"; + + // IDEA: Improve the algorithm that finds a place to summon creatures to take walls into account. + // IDEA: Replace all uses of Math.cos and Math.sin with MathHelper versions + // IDEA: API feature that automatically injects custom wand upgrades into wizardry's loot tables + // IDEA: Support for the @e selector in the /cast command (the methods already exist, just plug them in) + // IDEA: Dispensers shooting spells from scrolls! Imagine the possibilities! + // == Rendering overhaul! == + // IDEA: Make spirit animals fade away when dispelled + // IDEA: Use your newfound knowledge of OpenGL to render animated boxes around entities being buffed, similar to creeper charge + // IDEA: Redo RenderArc so it looks like lightning (it's literally a load of translucent boxes inside each other) + // IDEA: The arcane workbench could use a few particles + // IDEA: Add a box to the wizard armour model so that the robes bend when the wearer is sneaking + // IDEA: Flash particle (I think it exists in vanilla somewhere) on spell impact + // IDEA: Dust particles spray out from projectiles as they move + // IDEA: Colour fading for particles + // IDEA: Make lightning and other effects particles. There's no reason why you can't pass in a player argument and have the particles move with them! + + /* Minor bugs that need fixing at some point: + * - Player skin hat layer shows through wizard hats + * - Wizard armour breaks rather than just running out of mana (I can't seem to replicate this bug, but I have had + * it happen to me before...) + * - Shift-clicking a stack of special upgrades when in the arcane workbench causes the whole stack to be + * transferred when it should be just one (this is a bug with vanilla as well - try putting a stack of bottles into + * a brewing stand). I have at least made it so only one gets used now, so it has no impact on the game. + * - When a spell is on cooldown, you can't break blocks when holding a wand. */ + + // FIXME: Wizardry enchantments appear on enchanted books in dungeon chests when they shouldn't. + + // TODO: So somehow I have managed to overlook the fact that health is actually a float. What this means is that + // I can make the healing spells use damage multipliers - hurrah! + + // TODO: Switch from IInventory to IItemHandler (Or don't. It's only useful for automation really.) + // TODO: Triggering of inbuilt Forge events in relevant places? + // TODO: Implement custom events + // TODO: Rearrange the config gui and file + + // TODO: Have particles obey Minecraft's particle setting where appropriate + // (see https://github.com/RootsTeam/Embers/blob/master/src/main/java/teamroots/embers/particle/ParticleUtil.java) + + // NOTE: Add melee upgrades to loot tables when they are added. + + /** Static instance of the {@link Settings} object for Wizardry. */ + public static final Settings settings = new Settings(); + + /** Static instance of the {@link Logger} object for Wizardry. */ + public static Logger logger; + + //private static Pattern entityNamePattern; + + // EventManager + WizardryWorldGenerator generator = new WizardryWorldGenerator(); + + // The instance of wizardry that Forge uses. + @Instance(Wizardry.MODID) + public static Wizardry instance; + + // Location of the proxy code, used by Forge. + @SidedProxy(clientSide="electroblob.wizardry.client.ClientProxy", serverSide="electroblob.wizardry.CommonProxy") + public static CommonProxy proxy; + + @EventHandler + public void preInit(FMLPreInitializationEvent event){ + + logger = event.getModLog(); + + // The array in question no longer exists, so I'm pretty sure this isn't necessary any more. + // expandPotionTypesArray(); + + settings.initConfig(event); + + // Yes - by the looks of it, having an interface is completely unnecessary in this case. + CapabilityManager.INSTANCE.register(WizardData.class, new IStorage(){ + // These methods are only called by Capability.writeNBT() or Capability.readNBT(), which in turn are + // NEVER CALLED. Unless I'm missing some reflective invocation, that means this entire class serves only + // to allow capabilities to be saved and loaded manually. What that would be useful for I don't know. + // (If an API forces most users to write redundant code for no reason, it's not user friendly, is it?) + // ... well, that's my rant for today! + @Override public NBTBase writeNBT(Capability capability, WizardData instance, EnumFacing side){ return null; } + @Override public void readNBT(Capability capability, WizardData instance, EnumFacing side, NBTBase nbt){} + }, WizardData.class); + + WizardryRegistry.registerTileEntities(); + + WizardryRegistry.registerEntities(this); + // The check for the generateLoot setting is now done within this method. + WizardryRegistry.registerLoot(); + + // NOTE: Will need to be moved to init for 1.12, as will anything that needs to be after the registry events. + WizardryTabs.sort(); + + // Moved to preInit, because apparently it has to be here now. + proxy.registerRenderers(); + + } + + @EventHandler + public void init(FMLInitializationEvent event){ + + settings.initConfigExtras(); + + proxy.registerKeyBindings(); + + // Event Handlers + GameRegistry.registerWorldGenerator(generator, 0); + MinecraftForge.EVENT_BUS.register(new WizardryKeyHandler()); + MinecraftForge.EVENT_BUS.register(instance); + proxy.registerSpellHUD(); // This can't easily be converted to use the new @Mod.EventBusSubscriber system + NetworkRegistry.INSTANCE.registerGuiHandler(this, new WizardryGuiHandler()); + WizardryPacketHandler.initPackets(); + + // Achievements + AchievementPage.registerAchievementPage(WizardryAchievements.WIZARDRY_ACHIEVEMENT_PAGE); + + // Recipes + WizardryRegistry.registerRecipes(); + + proxy.initMixedFontRenderer(); + } + + @EventHandler + public void postInit(FMLPostInitializationEvent event){ + // This needs to be here or it won't necessarily include all the mods' entities. + // TODO: Re-implement this when the Forge bug is fixed. + /* Doesn't seem to be doing anything... + String entityNames = ""; + for(Object name : EntityList.classToStringMapping.values()){ + if(name instanceof String){ + entityNames = entityNames + name + '|'; + } + } + // Cuts off the last '|' + entityNames = entityNames.substring(0, entityNames.length()-1); + entityNamePattern = Pattern.compile(entityNames); + */ + proxy.initialiseLayers(); + } + + @EventHandler + public void serverStarting(FMLServerStartingEvent event){ + event.registerServerCommand(new CommandCastSpell()); + event.registerServerCommand(new CommandSetAlly()); + event.registerServerCommand(new CommandViewAllies()); + event.registerServerCommand(new CommandDiscoverSpell()); + } + + @SubscribeEvent + public void onConfigChanged(net.minecraftforge.fml.client.event.ConfigChangedEvent.OnConfigChangedEvent event){ + if(event.getModID().equals(Wizardry.MODID)){ + settings.saveConfigChanges(); + // All of the synchronised settings require a world restart anyway so this doesn't need syncing. + } + } + + // 2.1 changed some item ids, so this fixes them for existing worlds + // NOTE: Needs changing to RegistryEvent.MissingMapping in 1.12, or just removing since nobody updates minecraft + // versions when using mods. + @EventHandler + public static void onMissingMappingEvent(FMLMissingMappingsEvent event){ + // Just get, not getAll, since the mod id didn't change! + for(FMLMissingMappingsEvent.MissingMapping mapping : event.get()){ + if(mapping.type == Type.ITEM && mapping.resourceLocation.getResourceDomain().equals(Wizardry.MODID)){ + + Item replacement = null; + + switch(mapping.resourceLocation.getResourcePath()){ + + case "wand_basic": replacement = WizardryItems.magic_wand; break; + case "wand_basic_fire": replacement = WizardryItems.basic_fire_wand; break; + case "wand_basic_ice": replacement = WizardryItems.basic_ice_wand; break; + case "wand_basic_lightning": replacement = WizardryItems.basic_lightning_wand; break; + case "wand_basic_necromancy": replacement = WizardryItems.basic_necromancy_wand; break; + case "wand_basic_earth": replacement = WizardryItems.basic_earth_wand; break; + case "wand_basic_sorcery": replacement = WizardryItems.basic_sorcery_wand; break; + case "wand_basic_healing": replacement = WizardryItems.basic_healing_wand; break; + case "wand_apprentice": replacement = WizardryItems.apprentice_wand; break; + case "wand_apprentice_fire": replacement = WizardryItems.apprentice_fire_wand; break; + case "wand_apprentice_ice": replacement = WizardryItems.apprentice_ice_wand; break; + case "wand_apprentice_lightning": replacement = WizardryItems.apprentice_lightning_wand; break; + case "wand_apprentice_necromancy": replacement = WizardryItems.apprentice_necromancy_wand; break; + case "wand_apprentice_earth": replacement = WizardryItems.apprentice_earth_wand; break; + case "wand_apprentice_sorcery": replacement = WizardryItems.apprentice_sorcery_wand; break; + case "wand_apprentice_healing": replacement = WizardryItems.apprentice_healing_wand; break; + case "wand_advanced": replacement = WizardryItems.advanced_wand; break; + case "wand_advanced_fire": replacement = WizardryItems.advanced_fire_wand; break; + case "wand_advanced_ice": replacement = WizardryItems.advanced_ice_wand; break; + case "wand_advanced_lightning": replacement = WizardryItems.advanced_lightning_wand; break; + case "wand_advanced_necromancy": replacement = WizardryItems.advanced_necromancy_wand; break; + case "wand_advanced_earth": replacement = WizardryItems.advanced_earth_wand; break; + case "wand_advanced_sorcery": replacement = WizardryItems.advanced_sorcery_wand; break; + case "wand_advanced_healing": replacement = WizardryItems.advanced_healing_wand; break; + case "wand_master": replacement = WizardryItems.master_wand; break; + case "wand_master_fire": replacement = WizardryItems.master_fire_wand; break; + case "wand_master_ice": replacement = WizardryItems.master_ice_wand; break; + case "wand_master_lightning": replacement = WizardryItems.master_lightning_wand; break; + case "wand_master_necromancy": replacement = WizardryItems.master_necromancy_wand; break; + case "wand_master_earth": replacement = WizardryItems.master_earth_wand; break; + case "wand_master_sorcery": replacement = WizardryItems.master_sorcery_wand; break; + case "wand_master_healing": replacement = WizardryItems.master_healing_wand; break; + case "upgrade_storage": replacement = WizardryItems.storage_upgrade; break; + case "upgrade_siphon": replacement = WizardryItems.siphon_upgrade; break; + case "upgrade_condenser": replacement = WizardryItems.condenser_upgrade; break; + case "upgrade_range": replacement = WizardryItems.range_upgrade; break; + case "upgrade_duration": replacement = WizardryItems.duration_upgrade; break; + case "upgrade_cooldown": replacement = WizardryItems.cooldown_upgrade; break; + case "upgrade_blast": replacement = WizardryItems.blast_upgrade; break; + case "upgrade_attunement": replacement = WizardryItems.attunement_upgrade; break; + // If it didn't match any of the ones that changed, do nothing. + default: return; + } + // No need to log, Forge does that. + mapping.remap(replacement); + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/WizardryEventHandler.java b/src/main/java/electroblob/wizardry/WizardryEventHandler.java new file mode 100644 index 00000000..8cae85ed --- /dev/null +++ b/src/main/java/electroblob/wizardry/WizardryEventHandler.java @@ -0,0 +1,938 @@ +package electroblob.wizardry; + +import java.util.List; +import java.util.Map; + +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.enchantment.Imbuement; +import electroblob.wizardry.entity.EntityArc; +import electroblob.wizardry.entity.construct.EntityBubble; +import electroblob.wizardry.entity.construct.EntityDecay; +import electroblob.wizardry.entity.living.EntityEvilWizard; +import electroblob.wizardry.entity.living.EntityWizard; +import electroblob.wizardry.entity.living.ISpellCaster; +import electroblob.wizardry.entity.living.ISummonedCreature; +import electroblob.wizardry.item.IConjuredItem; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.item.ItemWizardArmour; +import electroblob.wizardry.potion.ICustomPotionParticles; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardryAchievements; +import electroblob.wizardry.registry.WizardryBlocks; +import electroblob.wizardry.registry.WizardryEnchantments; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.spell.Clairvoyance; +import electroblob.wizardry.spell.FreezingWeapon; +import electroblob.wizardry.spell.Intimidate; +import electroblob.wizardry.spell.MindControl; +import electroblob.wizardry.spell.ShadowWard; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.IElementalDamage; +import electroblob.wizardry.util.IndirectMinionDamage; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.MinionDamage; +import electroblob.wizardry.util.WandHelper; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityCreature; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.monster.EntityCreeper; +import net.minecraft.entity.monster.IMob; +import net.minecraft.entity.passive.EntityPig; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.projectile.EntityArrow; +import net.minecraft.init.Blocks; +import net.minecraft.init.SoundEvents; +import net.minecraft.inventory.ContainerPlayer; +import net.minecraft.inventory.ContainerWorkbench; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemBow; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemSword; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EntityDamageSource; +import net.minecraft.util.EntityDamageSourceIndirect; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; +import net.minecraft.world.storage.loot.LootEntry; +import net.minecraft.world.storage.loot.LootEntryTable; +import net.minecraft.world.storage.loot.LootPool; +import net.minecraft.world.storage.loot.RandomValueRange; +import net.minecraft.world.storage.loot.conditions.LootCondition; +import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.event.AttachCapabilitiesEvent; +import net.minecraftforge.event.LootTableLoadEvent; +import net.minecraftforge.event.entity.EntityJoinWorldEvent; +import net.minecraftforge.event.entity.EntityStruckByLightningEvent; +import net.minecraftforge.event.entity.item.ItemTossEvent; +import net.minecraftforge.event.entity.living.LivingAttackEvent; +import net.minecraftforge.event.entity.living.LivingDeathEvent; +import net.minecraftforge.event.entity.living.LivingDropsEvent; +import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; +import net.minecraftforge.event.entity.living.LivingHurtEvent; +import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent; +import net.minecraftforge.event.entity.player.BonemealEvent; +import net.minecraftforge.event.entity.player.EntityItemPickupEvent; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.event.entity.player.PlayerEvent.BreakSpeed; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.event.world.WorldEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent; + +// TODO: This class is waaaay too long. We need to either: +// - Split it into several separate handlers, each with a logical area to deal with +// - Remove most of the stuff, and have individual spell, item, block, entity, etc. classes handle their own events +// - Keep all the methods, but delegate near-repeated behaviours to their individual spell/potion/whatever classes + +/** + * Wizardry's main event handler class for common code. + * @author Electroblob + * @since Wizardry 1.0 + */ +@Mod.EventBusSubscriber +public final class WizardryEventHandler { + + @SubscribeEvent + // The type parameter here has to be Entity, not EntityPlayer, or the event won't get fired. + public static void onCapabilityLoad(AttachCapabilitiesEvent event){ + + if(event.getObject() instanceof EntityPlayer) + event.addCapability(new ResourceLocation(Wizardry.MODID, "WizardData"), new WizardData.Provider((EntityPlayer)event.getObject())); + + // This demonstrates why capabilities are badly structured: The following code compiles, but what it does is put + // a player into a CapabilityDispatcher, which is in turn stored in that very same player, which makes no sense + // at all! + //event.addCapability(new ResourceLocation(Wizardry.MODID, "WizardData"), event.getObject()); + } + + @SubscribeEvent + public static void onPlayerCloneEvent(PlayerEvent.Clone event){ + + WizardData newData = WizardData.get(event.getEntityPlayer()); + WizardData oldData = WizardData.get(event.getOriginal()); + + newData.copyFrom(oldData, event.isWasDeath()); + } + + @SubscribeEvent + public static void onWorldLoadEvent(WorldEvent.Load event){ + if(!event.getWorld().isRemote && event.getWorld().provider.getDimension() == 0){ + // Called to initialise the spell glyph data when a world loads, if it isn't already. + // NOTE: Do we actually need this, or can we just let it initialise the first time it is needed? (see below) + SpellGlyphData.get(event.getWorld()); + } + } + + // IDEA: Config option allowing users to specify loot locations + private static final String[] LOOT_INJECTION_LOCATIONS = { + "minecraft:chests/simple_dungeon", + "minecraft:chests/abandoned_mineshaft", + "minecraft:chests/desert_pyramid", + "minecraft:chests/jungle_temple", + "minecraft:chests/stronghold_corridor", + "minecraft:chests/stronghold_crossing", + "minecraft:chests/stronghold_library", + "minecraft:chests/igloo_chest" + }; + + @SubscribeEvent + public static void onLootTableLoadEvent(LootTableLoadEvent event){ + if(Wizardry.settings.generateLoot){ + for(String location : LOOT_INJECTION_LOCATIONS){ + if(event.getName().toString().matches(location)){ + event.getTable().addPool(getAdditive("wizardry:chests/dungeon_additions")); + } + } + } + } + + private static LootPool getAdditive(String entryName){ + return new LootPool(new LootEntry[] { getAdditiveEntry(entryName, 1) }, new LootCondition[0], new RandomValueRange(1), new RandomValueRange(0, 1), Wizardry.MODID + "_additive_pool"); + } + + private static LootEntryTable getAdditiveEntry(String name, int weight){ + return new LootEntryTable(new ResourceLocation(name), weight, 0, new LootCondition[0], Wizardry.MODID + "_additive_entry"); + } + + /* There is a subtle but important difference between LivingAttackEvent and LivingHurtEvent - LivingAttackEvent + * fires immediately when attackEntityFrom is called, whereas LivingHurtEvent only fires if the attack actually + * succeeded, i.e. if the entity in question takes damage (though the event is fired before that so you can cancel + * the damage). Things are processed in the following order: + * * LivingAttackEvent * + * - Invulnerability + * - Already-dead-ness + * - Fire resistance + * - Helmets vs. falling things + * - Hurt resistant time + * - Invulnerability (again) + * * LivingHurtEvent * + * - Armour + * - Potions + * - Health is finally changed + * Of course, there are no guarantees that other mods hooking into these two events will be called before or after + * yours, but you can have some degree of control by choosing which event to use. + * EDIT: Actually, there are. Firstly, you can set a priority in the @SubscribeEvent annotation which defines how + * early (higher priority) or late (lower priority) the method is called. Methods with the same priority are sorted + * alphabetically by mod id (so it's safe to assume wizardry would be fairly late on!). I wonder if there are any + * conventions for what sort of things take what priority...? */ + + @SubscribeEvent + public static void onLivingAttackEvent(LivingAttackEvent event){ + + // Rather than bother overriding entire attack methods in ISummonedCreature implementations, it's easier (and + // more robust) to use LivingAttackEvent to modify the damage source. + if(event.getSource().getEntity() instanceof ISummonedCreature){ + + EntityLivingBase summoner = ((ISummonedCreature)event.getSource().getEntity()).getCaster(); + + if(summoner != null){ + + event.setCanceled(true); + DamageSource newSource = event.getSource(); + // Copies over the original DamageType if appropriate. + DamageType type = event.getSource() instanceof IElementalDamage ? ((IElementalDamage)event.getSource()).getType() : DamageType.MAGIC; + // Copies over the original isRetaliatory flag if appropriate. + boolean isRetaliatory = event.getSource() instanceof IElementalDamage && ((IElementalDamage)event.getSource()).isRetaliatory(); + + // All summoned creatures are classified as magic, so it makes sense to do it this way. + if(event.getSource() instanceof EntityDamageSourceIndirect){ + newSource = new IndirectMinionDamage(event.getSource().damageType, event.getSource().getSourceOfDamage(), event.getSource().getEntity(), summoner, type, isRetaliatory); + }else if(event.getSource() instanceof EntityDamageSource){ + // Name is copied over so it uses the appropriate vanilla death message + newSource = new MinionDamage(event.getSource().damageType, event.getSource().getEntity(), summoner, type, isRetaliatory); + } + + // Copy over any relevant 'attributes' the original DamageSource might have had. + if(event.getSource().isExplosion()) newSource.setExplosion(); + if(event.getSource().isFireDamage()) newSource.setFireDamage(); + if(event.getSource().isProjectile()) newSource.setProjectile(); + + // For some reason Minecraft calculates knockback relative to DamageSource#getEntity. In vanilla this + // is unnoticeable, but it looks a bit weird with summoned creatures involved - so let's fix that! + if(WizardryUtilities.attackEntityWithoutKnockback(event.getEntity(), newSource, event.getAmount())){ + WizardryUtilities.applyStandardKnockback(event.getSource().getEntity(), event.getEntityLiving()); + ((ISummonedCreature)event.getSource().getEntity()).onSuccessfulAttack(event.getEntityLiving()); + } + + return; + } + } + + // Prevents any damage to allies from magic if friendly fire is enabled + if(!Wizardry.settings.friendlyFire && event.getSource() != null && event.getSource().getEntity() instanceof EntityPlayer + && event.getEntity() instanceof EntityPlayer && event.getSource() instanceof IElementalDamage){ + if(WizardryUtilities.isPlayerAlly((EntityPlayer)event.getSource().getEntity(), (EntityPlayer)event.getEntity())){ + event.setCanceled(true); + // I think this ought to be here, since if the event is cancelled nothing else needs to happen. + return; + } + } + + if(event.getSource() instanceof IElementalDamage){ + if(MagicDamage.isEntityImmune(((IElementalDamage)event.getSource()).getType(), event.getEntity())){ + event.setCanceled(true); + // I would have liked to have done the 'resist' chat message here, but I overlooked the fact that I + // would need an instance of the spell to get its display name! + return; + } + // One convenient side effect of the new damage type system is that I can get rid of all the places where + // creepers are charged and just put them here under shock damage - this is precisely the sort of + // repetitive code I was trying to get rid of, since errors can (and did!) occur. + if(event.getEntityLiving() instanceof EntityCreeper && !((EntityCreeper)event.getEntityLiving()).getPowered() + && ((IElementalDamage)event.getSource()).getType() == DamageType.SHOCK){ + // Charges creepers when they are hit by shock damage + WizardryUtilities.chargeCreeper((EntityCreeper)event.getEntityLiving()); + // Gives the player that caused the shock damage the 'It's Gonna Blow' achievement + if(event.getSource().getEntity() instanceof EntityPlayer){ + ((EntityPlayer)event.getSource().getEntity()).addStat(WizardryAchievements.charge_creeper); + } + } + } + + // Bursts bubble when the creature inside takes damage + if(event.getEntityLiving().getRidingEntity() instanceof EntityBubble && + !((EntityBubble)event.getEntityLiving().getRidingEntity()).isDarkOrb){ + event.getEntityLiving().getRidingEntity().playSound(SoundEvents.ENTITY_ITEM_PICKUP, 1.5f, 1.0f); + event.getEntityLiving().getRidingEntity().setDead(); + } + + // Prevents all unblockable damage while transience is active + if(event.getEntityLiving().isPotionActive(WizardryPotions.transience) && event.getSource() != null && !event.getSource().isUnblockable()){ + event.setCanceled(true); + // Again, I think this ought to be here, since if the event is cancelled nothing else needs to happen. + return; + } + + if(event.getSource() != null && event.getSource().getEntity() instanceof EntityLivingBase){ + + // Cancels the mind trick effect if the creature takes damage + // This has been moved to within the (event.getSource().getEntity() instanceof EntityLivingBase) check so it doesn't + // crash the game with a ConcurrentModificationException. If you think about it, mind trick only ought to be + // cancelled if something attacks the entity since potions, drowning, cacti etc. don't affect the targeting. + if(event.getEntityLiving().isPotionActive(WizardryPotions.mind_trick)){ + event.getEntityLiving().removePotionEffect(WizardryPotions.mind_trick); + } + + // 'Revenge' effects + + EntityLivingBase attacker = (EntityLivingBase)event.getSource().getEntity(); + World world = event.getEntityLiving().worldObj; + + if(event.getEntityLiving().isPotionActive(WizardryPotions.fireskin) && !event.getSource().isProjectile()){ + if(!MagicDamage.isEntityImmune(DamageType.FIRE, event.getEntityLiving())) attacker.setFire(5); + } + + if(event.getEntityLiving().isPotionActive(WizardryPotions.ice_shroud) && !event.getSource().isProjectile()){ + if(!MagicDamage.isEntityImmune(DamageType.FROST, event.getEntityLiving())) + attacker.addPotionEffect(new PotionEffect(WizardryPotions.frost, 100, 0)); + } + + if(event.getEntityLiving().isPotionActive(WizardryPotions.static_aura) && !event.getSource().isProjectile()){ + if(!world.isRemote){ + EntityArc arc = new EntityArc(world); + arc.setEndpointCoords(event.getEntityLiving().posX, event.getEntityLiving().posY + 1, event.getEntityLiving().posZ, + attacker.posX, attacker.posY + attacker.height/2, attacker.posZ); + world.spawnEntityInWorld(arc); + }else{ + for(int i=0;i<8;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, attacker.posX + world.rand.nextFloat() - 0.5, attacker.getEntityBoundingBox().minY + attacker.height/2 + world.rand.nextFloat()*2 - 1, attacker.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, attacker.posX + world.rand.nextFloat() - 0.5, attacker.getEntityBoundingBox().minY + attacker.height/2 + world.rand.nextFloat()*2 - 1, attacker.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); + } + } + + attacker.attackEntityFrom(MagicDamage.causeDirectMagicDamage(event.getEntityLiving(), DamageType.SHOCK, true), 4.0f); + attacker.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); + } + + // Shadow ward + if(event.getEntityLiving() instanceof EntityPlayer){ + + ItemStack wand = event.getEntityLiving().getActiveItemStack(); + + if(wand != null && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand + && WandHelper.getCurrentSpell(wand) instanceof ShadowWard && !event.getSource().isUnblockable()){ + + event.setCanceled(true); + // This DamageSource.magic and not event.getSource() because the latter would cause an infinite loop. + event.getEntityLiving().attackEntityFrom(DamageSource.magic, event.getAmount()/2); + attacker.attackEntityFrom(MagicDamage.causeDirectMagicDamage(event.getEntityLiving(), DamageType.MAGIC, true), event.getAmount()/2); + } + } + + // Transience + if(attacker.isPotionActive(WizardryPotions.transience)){ + event.setCanceled(true); + } + } + } + + @SubscribeEvent + public static void onLivingHurtEvent(LivingHurtEvent event){ + + // Curse of soulbinding + if(!event.getEntity().worldObj.isRemote && event.getEntityLiving() instanceof EntityPlayer && !event.getSource().isUnblockable()){ + WizardData properties = WizardData.get((EntityPlayer)event.getEntityLiving()); + if(properties != null){ + properties.damageAllSoulboundCreatures(event.getAmount()); + } + } + + // Flaming and freezing swords + if(event.getSource().getEntity() instanceof EntityLivingBase){ + + EntityLivingBase attacker = (EntityLivingBase)event.getSource().getEntity(); + + // Players can only ever attack with their main hand, so this is the right method to use here. + if(attacker.getHeldItemMainhand() != null && attacker.getHeldItemMainhand().getItem() instanceof ItemSword){ + + int level = EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.flaming_weapon, attacker.getHeldItemMainhand()); + + if(level > 0 && !MagicDamage.isEntityImmune(DamageType.FIRE, event.getEntityLiving())) + event.getEntityLiving().setFire(level*4); + + level = EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.freezing_weapon, attacker.getHeldItemMainhand()); + // Frost lasts for longer because it doesn't do any actual damage + if(level > 0 && !MagicDamage.isEntityImmune(DamageType.FROST, event.getEntityLiving())) + event.getEntityLiving().addPotionEffect(new PotionEffect(WizardryPotions.frost, level*200, 0)); + } + } + + // Freezing bow + if(event.getSource().getSourceOfDamage() instanceof EntityArrow && event.getSource().getSourceOfDamage().getEntityData() != null){ + + int level = event.getSource().getSourceOfDamage().getEntityData().getInteger(FreezingWeapon.FREEZING_ARROW_NBT_KEY); + + if(level > 0 && !MagicDamage.isEntityImmune(DamageType.FROST, event.getEntityLiving())) + event.getEntityLiving().addPotionEffect(new PotionEffect(WizardryPotions.frost, level*150, 0)); + } + + // Damage scaling + if(event.getSource() != null && event.getSource() instanceof IElementalDamage){ + + if(event.getSource().getEntity() instanceof EntityPlayer){ + event.setAmount((float)(event.getAmount() * Wizardry.settings.playerDamageScale)); + }else{ + event.setAmount((float)(event.getAmount() * Wizardry.settings.npcDamageScale)); + } + } + } + + @SubscribeEvent + public static void onBlockPlaceEvent(BlockEvent.PlaceEvent event){ + + if(event.getPlayer().isPotionActive(WizardryPotions.transience)){ + event.setCanceled(true); + return; + } + + // Spectral blocks cannot be built on + if(event.getPlacedAgainst() == WizardryBlocks.spectral_block){ + event.setCanceled(true); + return; + } + } + + @SubscribeEvent + public static void onBlockBreakEvent(BlockEvent.BreakEvent event){ + + if(event.getPlayer().isPotionActive(WizardryPotions.transience)){ + event.setCanceled(true); + return; + } + + // Makes wizards angry if a player breaks a block in their tower + if(!(event.getPlayer() instanceof FakePlayer)){ + + List wizards = WizardryUtilities.getEntitiesWithinRadius(64, event.getPos().getX(), + event.getPos().getY(), event.getPos().getZ(), event.getWorld(), EntityWizard.class); + + if(!wizards.isEmpty()){ + for(EntityWizard wizard : wizards){ + if(wizard.isBlockPartOfTower(event.getPos())){ + wizard.setRevengeTarget(event.getPlayer()); + event.getPlayer().addStat(WizardryAchievements.anger_wizard); + } + } + } + } + } + + @SubscribeEvent + public static void onEntityStruckByLightningEvent(EntityStruckByLightningEvent event){ + + if(event.getLightning().getEntityData() != null && event.getLightning().getEntityData().hasKey("summoningPlayer")){ + + EntityPlayer player = (EntityPlayer)WizardryUtilities.getEntityByUUID(event.getLightning().worldObj, event.getLightning().getEntityData().getUniqueId("summoningPlayer")); + + if(event.getEntity() instanceof EntityCreeper){ + player.addStat(WizardryAchievements.charge_creeper); + } + + if(event.getEntity() instanceof EntityPig){ + player.addStat(WizardryAchievements.frankenstein); + } + } + + } + + @SubscribeEvent + public static void onLivingUpdateEvent(LivingUpdateEvent event){ + + // TODO: Move the player stuff to a PlayerTickEvent and separate methods out for clarity + + if(event.getEntityLiving() instanceof EntityPlayer){ + + EntityPlayer entityplayer = (EntityPlayer)event.getEntityLiving(); + + if(WizardData.get(entityplayer) != null){ + WizardData.get(entityplayer).update(); + } + + if(entityplayer.openContainer instanceof ContainerWorkbench){ + craftingTableTick(entityplayer); + } + + if(entityplayer.openContainer instanceof ContainerPlayer){ + // Unfortunately I have no choice but to call this method every tick when the player isn't using another + // inventory, since the only thing tracking whether the player is looking at their inventory is the GUI + // itself, which is client-side only. + playerInventoryTick(entityplayer); + } + + testForArmourSet:{ + for(ItemStack stack : entityplayer.getArmorInventoryList()){ + if(stack == null || !(stack.getItem() instanceof ItemWizardArmour)){ + break testForArmourSet; + } + } + entityplayer.addStat(WizardryAchievements.armour_set); + } + + } + + if(event.getEntityLiving().worldObj.isRemote){ + // Behold the power of interfaces! TODO: Backport. + for(PotionEffect effect : event.getEntityLiving().getActivePotionEffects()){ + + if(effect.getPotion() instanceof ICustomPotionParticles && effect.doesShowParticles()){ + + double x = event.getEntityLiving().posX + (event.getEntityLiving().worldObj.rand.nextDouble() - 0.5)*event.getEntityLiving().width; + double y = event.getEntityLiving().getEntityBoundingBox().minY + event.getEntityLiving().worldObj.rand.nextDouble()*event.getEntityLiving().height; + double z = event.getEntityLiving().posZ + (event.getEntityLiving().worldObj.rand.nextDouble() - 0.5)*event.getEntityLiving().width; + + ((ICustomPotionParticles)effect.getPotion()).spawnCustomParticle(event.getEntityLiving().worldObj, x, y, z); + } + } + + // Client-side continuous spell casting for NPCs + + if(event.getEntity() instanceof ISpellCaster && event.getEntity() instanceof EntityLiving){ + + Spell spell = ((ISpellCaster)event.getEntity()).getContinuousSpell(); + + if(spell != null && spell != Spells.none){ + spell.cast(event.getEntity().worldObj, (EntityLiving)event.getEntity(), EnumHand.MAIN_HAND, 0, + // TODO: This implementation of modifiers relies on them being accessible client-side. + ((EntityLiving)event.getEntity()).getAttackTarget(), ((ISpellCaster)event.getEntity()).getModifiers()); + } + } + + }else{ + + if(event.getEntityLiving().isPotionActive(WizardryPotions.decay) && event.getEntityLiving().onGround && event.getEntityLiving().ticksExisted % Constants.DECAY_SPREAD_INTERVAL == 0){ + + List list = event.getEntityLiving().worldObj.getEntitiesWithinAABBExcludingEntity(event.getEntityLiving(), event.getEntityLiving().getEntityBoundingBox()); + + boolean flag = true; + + for(Object object : list){ + if(object instanceof EntityDecay) flag = false; + } + + if(flag){ + // The victim spreading the decay is the 'caster' here, so that it can actually wear off, otherwise it just gets infected with its own decay and the effect lasts forever. + event.getEntityLiving().worldObj.spawnEntityInWorld(new EntityDecay(event.getEntityLiving().worldObj, event.getEntityLiving().posX, event.getEntityLiving().posY, event.getEntityLiving().posZ, event.getEntityLiving())); + } + } + } + + // Mind Control + // This was added because something got changed in the AI classes which means LivingSetAttackTargetEvent doesn't + // get fired when I want it to... so I'm firing it myself. + if(event.getEntityLiving().isPotionActive(WizardryPotions.mind_control) && event.getEntityLiving() instanceof EntityLiving + && ((EntityLiving)event.getEntityLiving()).getAttackTarget() != null + && !((EntityLiving)event.getEntityLiving()).getAttackTarget().isEntityAlive()) + ((EntityLiving)event.getEntityLiving()).setAttackTarget(null); // Causes the event to be fired + + /* Old AI no longer exists! + + // Mind trick + + if(event.getEntityLiving().isPotionActive(Wizardry.mindTrick) && event.getEntityLiving() instanceof EntityLiving){ + // Old AI (this can't be done in onLivingSetAttackTargetEvent because that only fires for the new AI). + if(event.getEntityLiving() instanceof EntityCreature) ((EntityCreature)event.getEntityLiving()).setTarget(null); + } + + + // Mind control - old AI (this can't be done in onLivingSetAttackTargetEvent because that only fires for the new AI). + mindcontrol: + if(event.getEntityLiving().isPotionActive(Wizardry.mindControl) && event.getEntityLiving() instanceof EntityLiving){ + + NBTTagCompound entityNBT = event.getEntityLiving().getEntityData(); + + if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY)){ + + Entity caster = WizardryUtilities.getEntityByUUID(event.getEntity().worldObj, UUID.fromString(entityNBT.getString(MindControl.NBT_KEY))); + + if(caster instanceof EntityLivingBase){ + + if(MindControl.findMindControlTarget((EntityLiving)event.getEntityLiving(), (EntityLivingBase)caster, event.getEntity().worldObj)){ + // If it worked, skip setting the target to null. + break mindcontrol; + } + } + } + // If the caster couldn't be found or no valid target was found, this just acts like mind trick. + ((EntityLiving)event.getEntityLiving()).setAttackTarget(null); + } + */ + + // Intimidate + if(event.getEntityLiving().isPotionActive(WizardryPotions.fear) && event.getEntityLiving() instanceof EntityCreature){ + + NBTTagCompound entityNBT = event.getEntityLiving().getEntityData(); + EntityCreature creature = (EntityCreature)event.getEntityLiving(); + + if(entityNBT != null && entityNBT.hasKey(Intimidate.NBT_KEY)){ + + Entity caster = WizardryUtilities.getEntityByUUID(creature.worldObj, entityNBT.getUniqueId(Intimidate.NBT_KEY)); + + if(caster instanceof EntityLivingBase){ + Intimidate.runAway(creature, (EntityLivingBase)caster); + } + } + } + } + + @SubscribeEvent + public static void onBreakSpeedEvent(BreakSpeed event){ + if(event.getEntityPlayer().isPotionActive(WizardryPotions.frost)){ + // Amplifier + 1 because it starts at 0 + event.setNewSpeed(event.getOriginalSpeed() * (1 - Constants.FROST_FATIGUE_PER_LEVEL*(event.getEntityPlayer().getActivePotionEffect(WizardryPotions.frost).getAmplifier() + 1))); + } + } + + private static void playerInventoryTick(EntityPlayer player) { + + // Charges wand using mana flask. It is here rather than in the crafting handler so the result displays + // the proper damage before it is actually crafted. + + boolean flag = false; + ItemStack wand = null; + ItemStack armour = null; + IInventory craftMatrix = ((ContainerPlayer)player.openContainer).craftMatrix; + ItemStack outputItem = ((ContainerPlayer)player.openContainer).craftResult.getStackInSlot(0); + + for(int i = 0; i < craftMatrix.getSizeInventory(); i++){ + if(craftMatrix.getStackInSlot(i) != null){ + ItemStack itemstack = craftMatrix.getStackInSlot(i); + + if(itemstack.getItem() == WizardryItems.mana_flask){ + flag = true; + } + + if(itemstack.getItem() instanceof ItemWand){ + wand = itemstack; + } + + if(itemstack.getItem() instanceof ItemWizardArmour){ + armour = itemstack; + } + } + } + + if(outputItem != null && outputItem.getItem() instanceof ItemWand && flag && wand != null){ + outputItem.setTagCompound((wand.getTagCompound())); + if(wand.getItemDamage()-Constants.MANA_PER_FLASK < 0){ + outputItem.setItemDamage(0); + }else{ + outputItem.setItemDamage(wand.getItemDamage()-Constants.MANA_PER_FLASK); + } + } + + if(outputItem != null && outputItem.getItem() instanceof ItemWizardArmour && flag && armour != null){ + outputItem.setTagCompound((armour.getTagCompound())); + if(armour.getItemDamage()-Constants.MANA_PER_FLASK < 0){ + outputItem.setItemDamage(0); + }else{ + outputItem.setItemDamage(wand.getItemDamage()-Constants.MANA_PER_FLASK); + } + } + } + + private static void craftingTableTick(EntityPlayer player) { + + // Charges wand using mana flask. It is here rather than in the crafting handler so the result displays + // the proper damage before it is actually crafted. + + boolean flag = false; + ItemStack wand = null; + ItemStack armour = null; + IInventory craftMatrix = ((ContainerWorkbench)player.openContainer).craftMatrix; + ItemStack outputItem = ((ContainerWorkbench)player.openContainer).craftResult.getStackInSlot(0); + + for (int i = 0; i < craftMatrix.getSizeInventory(); i++){ + if(craftMatrix.getStackInSlot(i) != null){ + ItemStack itemstack = craftMatrix.getStackInSlot(i); + + if(itemstack.getItem() == WizardryItems.mana_flask){ + flag = true; + } + + if(itemstack.getItem() instanceof ItemWand){ + wand = itemstack; + } + + if(itemstack.getItem() instanceof ItemWizardArmour){ + armour = itemstack; + } + } + } + + if(outputItem != null && outputItem.getItem() instanceof ItemWand && flag && wand != null){ + outputItem.setTagCompound((wand.getTagCompound())); + if(wand.getItemDamage()-Constants.MANA_PER_FLASK < 0){ + outputItem.setItemDamage(0); + }else{ + outputItem.setItemDamage(wand.getItemDamage()-Constants.MANA_PER_FLASK); + } + } + + if(outputItem != null && outputItem.getItem() instanceof ItemWizardArmour && flag && armour != null){ + outputItem.setTagCompound((armour.getTagCompound())); + if(armour.getItemDamage()-Constants.MANA_PER_FLASK < 0){ + outputItem.setItemDamage(0); + }else{ + outputItem.setItemDamage(wand.getItemDamage()-Constants.MANA_PER_FLASK); + } + } + } + + @SubscribeEvent + public static void onLivingDeathEvent(LivingDeathEvent event){ + + if(event.getSource().getEntity() instanceof EntityPlayer){ + + EntityPlayer player = (EntityPlayer)event.getSource().getEntity(); + + for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(player)){ + + if(stack != null && stack.getItem() instanceof ItemWand && stack.isItemDamaged() && WandHelper.getUpgradeLevel(stack, WizardryItems.siphon_upgrade) > 0){ + int damage = stack.getItemDamage() - Constants.SIPHON_MANA_PER_LEVEL*WandHelper.getUpgradeLevel(stack, WizardryItems.siphon_upgrade) - player.worldObj.rand.nextInt(Constants.SIPHON_MANA_PER_LEVEL); + if(damage < 0) damage = 0; + stack.setItemDamage(damage); + break; + } + } + + if(event.getEntityLiving() == player && event.getSource() instanceof IElementalDamage){ + player.addStat(WizardryAchievements.self_destruct); + } + } + } + + @SubscribeEvent + public static void onPlayerLoggedInEvent(PlayerLoggedInEvent event){ + // When a player logs in, they are sent the glyph data and the server's settings. + if(event.player instanceof EntityPlayerMP){ + SpellGlyphData.get(event.player.worldObj).sync((EntityPlayerMP)event.player); + Wizardry.settings.sync((EntityPlayerMP)event.player); + } + } + + @SubscribeEvent + public static void onEntityJoinWorld(EntityJoinWorldEvent event){ + + if(!event.getEntity().worldObj.isRemote && event.getEntity() instanceof EntityPlayerMP){ + // Synchronises wizard data after loading. + WizardData data = WizardData.get((EntityPlayer)event.getEntity()); + if(data != null) data.sync(); + } + + // Rather long-winded (but necessary) way of getting an arrow just after it has been fired, checking if the bow + // that fired it has the imbuement enchantment, and applying extra damage accordingly. + if(!event.getEntity().worldObj.isRemote && event.getEntity() instanceof EntityArrow){ + + EntityArrow arrow = (EntityArrow)event.getEntity(); + + magicBow: + if(arrow.shootingEntity instanceof EntityLivingBase){ + + EntityLivingBase archer = (EntityLivingBase)arrow.shootingEntity; + + ItemStack bow = archer.getHeldItemMainhand(); + + if(bow == null || !(bow.getItem() instanceof ItemBow)){ + bow = archer.getHeldItemOffhand(); + // Break used because return would skip the entire method, bypassing anything that might be added + // further down. + if(bow == null || !(bow.getItem() instanceof ItemBow)) break magicBow; + } + + // Taken directly from ItemBow, so it works exactly the same as the power enchantment. + int level = EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.magic_bow, bow); + + if(level > 0){ + arrow.setDamage(arrow.getDamage() + (double)level * 0.5D + 0.5D); + } + + if(EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.flaming_weapon, bow) > 0){ + // Again, this is exactly what happens in ItemBow (flame is flame; level does nothing). + arrow.setFire(100); + } + + level = EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.freezing_weapon, bow); + + if(level > 0){ + if(arrow.getEntityData() != null){ + arrow.getEntityData().setInteger(FreezingWeapon.FREEZING_ARROW_NBT_KEY, level); + } + } + } + + } + } + + @SubscribeEvent + public static void onLivingDropsEvent(LivingDropsEvent event){ + // TODO: Really, this should be in a loot table (mob_additions), however I can't seem to find a way of + // automatically adding it to all subclasses of IMob. + // Evil wizards drop spell books themselves + if(event.getEntityLiving() instanceof IMob && !(event.getEntityLiving() instanceof EntityEvilWizard) + // TODO: Backport when you backport the new summoned creature system. + && !(event.getEntityLiving() instanceof ISummonedCreature) && event.getSource().getEntity() instanceof EntityPlayer + && Wizardry.settings.spellBookDropChance > 0){ + + // This does exactly what the entity drop method does, but with a different random number so that the + // spell book doesn't always drop with other rare drops. + int rareDropNumber = event.getEntity().worldObj.rand.nextInt(200) - event.getLootingLevel(); + if(rareDropNumber < Wizardry.settings.spellBookDropChance){ + // Drops a spell book + int id = WizardryUtilities.getStandardWeightedRandomSpellId(event.getEntity().worldObj.rand); + + event.getDrops().add(new EntityItem(event.getEntityLiving().worldObj, event.getEntityLiving().posX, event.getEntityLiving().posY, event.getEntityLiving().posZ, + new ItemStack(WizardryItems.spell_book, 1, id))); + } + } + + for(EntityItem item : event.getDrops()){ + + // Destroys conjured items if their caster dies. + if(item.getEntityItem().getItem() instanceof IConjuredItem){ + item.setDead(); + } + + // Instantly disenchants an imbued weapon if it is dropped when the player dies. + if(item.getEntityItem().isItemEnchanted()){ + + // No need to check what enchantments the item has, since remove() does nothing if the element does not exist. + Map enchantments = EnchantmentHelper.getEnchantments(item.getEntityItem()); + + // Removes the magic weapon enchantments from the enchantment map + // An excellent demonstration of the usefulness of both interfaces and Java 8. + enchantments.entrySet().removeIf(entry -> entry.getKey() instanceof Imbuement); + + // Applies the new enchantment map to the item + EnchantmentHelper.setEnchantments(enchantments, item.getEntityItem()); + } + } + } + + @SubscribeEvent + public static void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){ + // Mind trick + // If the target is null already, no need to set it to null, or infinite loops will occur. + if((event.getEntityLiving().isPotionActive(WizardryPotions.mind_trick) || event.getEntityLiving().isPotionActive(WizardryPotions.fear)) && event.getEntityLiving() instanceof EntityLiving && event.getTarget() != null){ + ((EntityLiving)event.getEntityLiving()).setAttackTarget(null); + } + + // Mind control + mindcontrol: + if(event.getEntityLiving().isPotionActive(WizardryPotions.mind_control) && event.getEntityLiving() instanceof EntityLiving){ + + NBTTagCompound entityNBT = event.getEntityLiving().getEntityData(); + + if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY + "Most")){ + + Entity caster = WizardryUtilities.getEntityByUUID(event.getEntity().worldObj, entityNBT.getUniqueId(MindControl.NBT_KEY)); + + // If the target that the event tried to set is already a valid mind control target, nothing happens. + if(event.getTarget() != null && WizardryUtilities.isValidTarget(caster, event.getTarget())) break mindcontrol; + + if(caster instanceof EntityLivingBase){ + + if(MindControl.findMindControlTarget((EntityLiving)event.getEntityLiving(), (EntityLivingBase)caster, event.getEntity().worldObj)){ + // If it worked, skip setting the target to null. + break mindcontrol; + } + } + } + // If the caster couldn't be found or no valid target was found, this just acts like mind trick. + // If the target is null already, no need to set it to null, or infinite loops will occur. + if(event.getTarget() != null) ((EntityLiving)event.getEntityLiving()).setAttackTarget(null); + } + } + + @SubscribeEvent + public static void onItemPickupEvent(EntityItemPickupEvent event){ + if(event.getItem().getEntityItem().getItem() == WizardryItems.magic_crystal){ + event.getEntityPlayer().addStat(WizardryAchievements.crystal, 1); + } + } + + @SubscribeEvent + public static void onItemTossEvent(ItemTossEvent event){ + + // Prevents conjured items being thrown by dragging and dropping outside the inventory. + if(event.getEntityItem().getEntityItem().getItem() instanceof IConjuredItem){ + event.setCanceled(true); + event.getPlayer().inventory.addItemStackToInventory(event.getEntityItem().getEntityItem()); + } + + // Instantly disenchants an imbued weapon if it is thrown on the ground. + if(event.getEntityItem().getEntityItem().isItemEnchanted()){ + + // No need to check what enchantments the item has, since remove() does nothing if the element does not exist. + Map enchantments = EnchantmentHelper.getEnchantments(event.getEntityItem().getEntityItem()); + + // Removes the magic weapon enchantments from the enchantment map + enchantments.entrySet().removeIf(entry -> entry.getKey() instanceof Imbuement); + + // Applies the new enchantment map to the item + EnchantmentHelper.setEnchantments(enchantments, event.getEntityItem().getEntityItem()); + } + } + + @SubscribeEvent + public static void onRightClickBlockEvent(PlayerInteractEvent.RightClickBlock event){ + + if(event.getEntityPlayer().isSneaking()){ + + // The event now has an ItemStack, which greatly simplifies hand-related stuff. + ItemStack wand = event.getItemStack(); + + if(wand != null && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof Clairvoyance){ + + WizardData properties = WizardData.get(event.getEntityPlayer()); + + if(properties != null){ + // THIS is why BlockPos is a thing - in 1.7.10 this requires a clumsy switch statement. + BlockPos pos = event.getPos().offset(event.getFace()); + + properties.setClairvoyancePoint(pos, event.getWorld().provider.getDimension()); + + if(!event.getWorld().isRemote){ + event.getEntityPlayer().addChatMessage(new TextComponentTranslation("spell.clairvoyance.confirm", Spells.clairvoyance.getNameForTranslationFormatted())); + } + + event.setCanceled(true); + } + } + } + } + + @SubscribeEvent + public static void onBonemealEvent(BonemealEvent event){ + // Grows crystal flowers when bonemeal is used on grass + if(event.getBlock().getBlock() == Blocks.GRASS){ + + BlockPos pos = event.getPos().add(event.getWorld().rand.nextInt(8) - event.getWorld().rand.nextInt(8), + event.getWorld().rand.nextInt(4) - event.getWorld().rand.nextInt(4), + event.getWorld().rand.nextInt(8) - event.getWorld().rand.nextInt(8)); + + if (event.getWorld().isAirBlock(new BlockPos(pos)) && (!event.getWorld().provider.getHasNoSky() || pos.getY() < 127) && WizardryBlocks.crystal_flower.canPlaceBlockAt(event.getWorld(), pos)) + { + event.getWorld().setBlockState(pos, WizardryBlocks.crystal_flower.getDefaultState(), 2); + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/WizardryGuiFactory.java b/src/main/java/electroblob/wizardry/WizardryGuiFactory.java new file mode 100644 index 00000000..0f7c0161 --- /dev/null +++ b/src/main/java/electroblob/wizardry/WizardryGuiFactory.java @@ -0,0 +1,36 @@ +package electroblob.wizardry; + +import java.util.Set; + +import electroblob.wizardry.client.GuiConfigWizardry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraftforge.fml.client.IModGuiFactory; + +public class WizardryGuiFactory implements IModGuiFactory { + + @Override + public void initialize(Minecraft minecraftInstance) + { + + } + + @Override + public Class mainConfigGuiClass() + { + return GuiConfigWizardry.class; + } + + @Override + public Set runtimeGuiCategories() + { + return null; + } + + @SuppressWarnings("deprecation") // It's a bit of Forge that hasn't been tidied up, but we have to implement it. + @Override + public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) + { + return null; + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/WizardryGuiHandler.java b/src/main/java/electroblob/wizardry/WizardryGuiHandler.java new file mode 100644 index 00000000..ad17f45d --- /dev/null +++ b/src/main/java/electroblob/wizardry/WizardryGuiHandler.java @@ -0,0 +1,66 @@ +package electroblob.wizardry; + +import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.item.ItemWizardHandbook; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; +import electroblob.wizardry.tileentity.ContainerPortableWorkbench; +import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.fml.common.network.IGuiHandler; + +public class WizardryGuiHandler implements IGuiHandler { + + /** Incrementable index for the gui ID */ + private static int nextGuiId = 0; + + public static final int SPELL_BOOK = nextGuiId++; + public static final int ARCANE_WORKBENCH = nextGuiId++; + public static final int WIZARD_HANDBOOK = nextGuiId++; + public static final int PORTABLE_CRAFTING = nextGuiId++; + + @Override + public Object getServerGuiElement(int id, EntityPlayer player, World world, + int x, int y, int z) { + if(id == ARCANE_WORKBENCH){ + TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z)); + if(tileEntity instanceof TileEntityArcaneWorkbench){ + return new ContainerArcaneWorkbench(player.inventory, (TileEntityArcaneWorkbench) tileEntity); + } + } + else if(id == PORTABLE_CRAFTING){ + return new ContainerPortableWorkbench(player.inventory, world, new BlockPos(x, y, z)); + } + return null; + } + + @Override + public Object getClientGuiElement(int id, EntityPlayer player, World world, + int x, int y, int z) { + if(id == ARCANE_WORKBENCH){ + TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z)); + if(tileEntity instanceof TileEntityArcaneWorkbench){ + return new electroblob.wizardry.client.GuiArcaneWorkbench(player.inventory, (TileEntityArcaneWorkbench) tileEntity); + } + } + else if(id == WIZARD_HANDBOOK + && ((player.getHeldItemMainhand() != null && player.getHeldItemMainhand().getItem() instanceof ItemWizardHandbook) + || (player.getHeldItemOffhand() != null && player.getHeldItemOffhand().getItem() instanceof ItemWizardHandbook))){ + return new electroblob.wizardry.client.GuiWizardHandbook(); + } + else if(id == SPELL_BOOK){ + if(player.getHeldItemMainhand() != null && player.getHeldItemMainhand().getItem() instanceof ItemSpellBook){ + return new electroblob.wizardry.client.GuiSpellBook(Spell.get(player.getHeldItemMainhand().getItemDamage())); + }else if(player.getHeldItemOffhand() != null && player.getHeldItemOffhand().getItem() instanceof ItemSpellBook){ + return new electroblob.wizardry.client.GuiSpellBook(Spell.get(player.getHeldItemOffhand().getItemDamage())); + } + } + else if(id == PORTABLE_CRAFTING){ + return new electroblob.wizardry.client.GuiPortableCrafting(player.inventory, world, new BlockPos(x, y, z)); + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/WizardryKeyHandler.java b/src/main/java/electroblob/wizardry/WizardryKeyHandler.java new file mode 100644 index 00000000..9cfc7ab9 --- /dev/null +++ b/src/main/java/electroblob/wizardry/WizardryKeyHandler.java @@ -0,0 +1,67 @@ +package electroblob.wizardry; + +import org.lwjgl.input.Keyboard; + +import electroblob.wizardry.client.ClientProxy; +import electroblob.wizardry.packet.PacketControlInput; +import electroblob.wizardry.packet.WizardryPacketHandler; +import net.minecraft.client.Minecraft; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.InputEvent; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; + +public class WizardryKeyHandler { + + boolean NkeyPressed = false; + boolean BkeyPressed = false; + boolean NkeyAlreadyPressed = false; + boolean BkeyAlreadyPressed = false; + + @SubscribeEvent + public void onKeyInput(InputEvent.KeyInputEvent event) { + + // Key pressed + if(Keyboard.getEventKeyState()){ + + if(Wizardry.proxy instanceof ClientProxy){ + + if(ClientProxy.NEXT_SPELL.isPressed() && Minecraft.getMinecraft().inGameHasFocus){ + if(!NkeyPressed){ + NkeyPressed = true; + }else{ + NkeyAlreadyPressed = true; + } + if(!NkeyAlreadyPressed){ + // Packet building + IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.NEXT_SPELL_KEY); + WizardryPacketHandler.net.sendToServer(msg); + } + } + + if(ClientProxy.PREVIOUS_SPELL.isPressed() && Minecraft.getMinecraft().inGameHasFocus){ + if(!BkeyPressed){ + BkeyPressed = true; + }else{ + BkeyAlreadyPressed = true; + } + if(!BkeyAlreadyPressed){ + // Packet building + IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.PREVIOUS_SPELL_KEY); + WizardryPacketHandler.net.sendToServer(msg); + } + } + } + } + + // Key released + else{ + if(NkeyPressed){ + NkeyPressed = false; + NkeyAlreadyPressed = false; + }else if(BkeyPressed){ + BkeyPressed = false; + BkeyAlreadyPressed = false; + } + } + } +} diff --git a/src/main/java/electroblob/wizardry/WizardryWorldGenerator.java b/src/main/java/electroblob/wizardry/WizardryWorldGenerator.java new file mode 100644 index 00000000..74fdab00 --- /dev/null +++ b/src/main/java/electroblob/wizardry/WizardryWorldGenerator.java @@ -0,0 +1,1546 @@ +package electroblob.wizardry; + +import java.util.HashSet; +import java.util.Random; +import java.util.Set; + +import org.apache.commons.lang3.ArrayUtils; + +import electroblob.wizardry.entity.living.EntityEvilWizard; +import electroblob.wizardry.entity.living.EntityWizard; +import electroblob.wizardry.registry.WizardryBlocks; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.block.BlockChest; +import net.minecraft.block.BlockColored; +import net.minecraft.block.BlockLeaves; +import net.minecraft.block.BlockLiquid; +import net.minecraft.block.BlockPlanks; +import net.minecraft.block.BlockSlab; +import net.minecraft.block.BlockTorch; +import net.minecraft.block.BlockSlab.EnumBlockHalf; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Biomes; +import net.minecraft.init.Blocks; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.EnumDyeColor; +import net.minecraft.item.ItemDoor; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.WorldServer; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.chunk.IChunkGenerator; +import net.minecraft.world.chunk.IChunkProvider; +import net.minecraft.world.gen.feature.WorldGenMinable; +import net.minecraft.world.storage.loot.LootContext; +import net.minecraft.world.storage.loot.LootTable; +import net.minecraftforge.common.BiomeDictionary; +import net.minecraftforge.common.IPlantable; +import net.minecraftforge.fml.common.IWorldGenerator; + +public class WizardryWorldGenerator implements IWorldGenerator { + + /** The string identifier for wizard tower chests, used in ChestGenHooks. */ + public static final String WIZARD_TOWER = Wizardry.MODID + "wizardTower"; + + @Override + public void generate(Random random, int chunkX, int chunkZ, World world, IChunkGenerator chunkGenerator, IChunkProvider chunkProvider){ + + for(int id : Wizardry.settings.oreDimensions){ + if(id == world.provider.getDimension()) this.addOreSpawn(WizardryBlocks.crystal_ore.getDefaultState(), world, random, chunkX * 16, chunkZ * 16, 16, 16, 5, 7, 5, 30); + } + + for(int id : Wizardry.settings.flowerDimensions){ + if(id == world.provider.getDimension()) this.generatePlant(WizardryBlocks.crystal_flower.getDefaultState(), world, random, chunkX * 16, chunkZ * 16, 2, 20); + } + + if(world.getWorldInfo().isMapFeaturesEnabled()){ + for(int id : Wizardry.settings.towerDimensions){ + if(id == world.provider.getDimension()) this.generateWizardTower(world, random, chunkX * 16, chunkZ * 16); + } + } + } + + /** + * Adds an Ore Spawn to Minecraft. Simply register all Ores to spawn with this method in your Generation method in your IWorldGeneration extending Class + * + * @param The Block to spawn + * @param The World to spawn in + * @param A Random object for retrieving random positions within the world to spawn the Block + * @param An int for passing the X-Coordinate for the Generation method + * @param An int for passing the Z-Coordinate for the Generation method + * @param An int for setting the maximum X-Coordinate values for spawning on the X-Axis on a Per-Chunk basis + * @param An int for setting the maximum Z-Coordinate values for spawning on the Z-Axis on a Per-Chunk basis + * @param An int for setting the maximum size of a vein + * @param An int for the Number of chances available for the Block to spawn per-chunk + * @param An int for the minimum Y-Coordinate height at which this block may spawn + * @param An int for the maximum Y-Coordinate height at which this block may spawn + **/ + public void addOreSpawn(IBlockState state, World world, Random random, int blockXPos, int blockZPos, int maxX, int maxZ, int maxVeinSize, int chancesToSpawn, int minY, int maxY) + { + //int maxPossY = minY + (maxY - 1); + assert maxY > minY: "The maximum Y must be greater than the Minimum Y"; + assert maxX > 0 && maxX <= 16: "addOreSpawn: The Maximum X must be greater than 0 and less than 16"; + assert minY > 0: "addOreSpawn: The Minimum Y must be greater than 0"; + assert maxY < 256 && maxY > 0: "addOreSpawn: The Maximum Y must be less than 256 but greater than 0"; + assert maxZ > 0 && maxZ <= 16: "addOreSpawn: The Maximum Z must be greater than 0 and less than 16"; + + int diffBtwnMinMaxY = maxY - minY; + for(int x = 0; x < chancesToSpawn; x++) + { + int posX = blockXPos + random.nextInt(maxX); + int posY = minY + random.nextInt(diffBtwnMinMaxY); + int posZ = blockZPos + random.nextInt(maxZ); + (new WorldGenMinable(state, maxVeinSize)).generate(world, random, new BlockPos(posX, posY, posZ)); + } + } + + /** + * Generates the specified plant randomly throughout the world. + * @param block The plant block + * @param world The world + * @param random A random instance + * @param x The x coord of the first block in the chunk + * @param z The y coord of the first block in the chunk + * @param chancesToSpawn Number of chances to spawn a flower patch + * @param groupSize The number of times to try generating a flower per flower patch spawn + */ + public void generatePlant(IBlockState state, World world, Random random, int x, int z, int chancesToSpawn, int groupSize){ + + for(int i = 0; i < chancesToSpawn; i++){ + int randPosX = x + random.nextInt(16); + int randPosY = random.nextInt(256); + int randPosZ = z + random.nextInt(16); + for (int l = 0; l < groupSize; ++l) + { + int i1 = randPosX + random.nextInt(8) - random.nextInt(8); + int j1 = randPosY + random.nextInt(4) - random.nextInt(4); + int k1 = randPosZ + random.nextInt(8) - random.nextInt(8); + + BlockPos pos = new BlockPos(i1, j1, k1); + + if(world.isBlockLoaded(pos) && world.isAirBlock(pos) && (!world.provider.getHasNoSky() || j1 < 127) && state.getBlock().canPlaceBlockOnSide(world, pos, EnumFacing.UP)){ + + world.setBlockState(pos, state, 2); + } + } + } + } + + /** + * Generates wizard towers randomly throughout the world. + */ + public void generateWizardTower(World world, Random random, int chunkX, int chunkZ){ + + // Allows the config file to set the rarity value to 0 to disable tower generation completely. + if(Wizardry.settings.towerRarity == 0) return; + + // Compensates for the lack of space in forests. Math.max is required since treeless biomes have treesPerChunk = -999 + //double treeFactor = 70 - Math.max((double)world.getBiomeGenForCoords(chunkX, chunkZ).theBiomeDecorator.treesPerChunk, 0) * 1.5d; + + // Multiplied by 70 to (roughly) retain the old rarity scale + if(random.nextInt((int)(Wizardry.settings.towerRarity * 70)) == 0){ + + BlockPos origin = new BlockPos(chunkX + random.nextInt(16), 0, chunkZ + random.nextInt(16)); + + // Despite what its name suggests, this method does not return the position of a liquid. It is in fact + // exactly what is needed here since it is used for placing villages and stuff, and doesn't include leaves + // or other foliage. + origin = origin.up(world.getTopSolidOrLiquidBlock(origin).getY() - 1); + + int[][][] towerBlueprint = towerBlueprintSmall; + + switch(random.nextInt(4)){ + case 0: towerBlueprint = towerBlueprintSmall; break; + case 1: towerBlueprint = towerBlueprintMedium; break; + case 2: towerBlueprint = towerBlueprintTall; break; + case 3: towerBlueprint = towerBlueprintDouble; break; + } + + // 0 = West, 1 = North, 2 = East, 3 = South (The way you would face when walking out of the door) + EnumFacing orientation = EnumFacing.getHorizontal(random.nextInt(4)); + boolean flip = random.nextBoolean(); + + if(checkSpaceForTower(world, origin, towerBlueprint, orientation, flip)){ + + // == Setup == + + boolean evilWizard = random.nextInt(5) == 0; + + IBlockState wallMaterial = Blocks.COBBLESTONE.getDefaultState(); + BlockPlanks.EnumType woodType = BlockPlanks.EnumType.OAK; + + Biome biome = world.getBiome(origin); + + // The order of these is somewhat important in that biomes can be many types, so the last of these checks + // that is true gets priority. Generally speaking, the later the check, the more specific it is. + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.DENSE)) wallMaterial = Blocks.MOSSY_COBBLESTONE.getDefaultState(); + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.SWAMP)) wallMaterial = Blocks.MOSSY_COBBLESTONE.getDefaultState(); + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.SANDY)) wallMaterial = Blocks.SANDSTONE.getDefaultState(); + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.NETHER)) wallMaterial = Blocks.NETHER_BRICK.getDefaultState(); + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.MOUNTAIN)) wallMaterial = Blocks.STONEBRICK.getDefaultState(); + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.MESA)) wallMaterial = Blocks.HARDENED_CLAY.getDefaultState(); + + // Unfortunately, I can't check all the wood types with the biome dictionary + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.CONIFEROUS)) woodType = BlockPlanks.EnumType.SPRUCE; + if(biome == Biomes.BIRCH_FOREST || biome == Biomes.BIRCH_FOREST_HILLS) woodType = BlockPlanks.EnumType.BIRCH; + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.JUNGLE)) woodType = BlockPlanks.EnumType.JUNGLE; + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.SAVANNA)) woodType = BlockPlanks.EnumType.ACACIA; + // Not technically a tree type, but I think it fits quite well anyway + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.SPOOKY)) woodType = BlockPlanks.EnumType.DARK_OAK; + + IBlockState[] blockStateList = new IBlockState[]{ + null, + Blocks.AIR.getDefaultState(), + Blocks.PLANKS.getDefaultState().withProperty(BlockPlanks.VARIANT, woodType), + Blocks.BOOKSHELF.getDefaultState(), + Blocks.STAINED_HARDENED_CLAY.getDefaultState().withProperty(BlockColored.COLOR, + EnumDyeColor.values()[random.nextInt(EnumDyeColor.values().length)]), + Blocks.WOODEN_SLAB.getDefaultState().withProperty(BlockSlab.HALF, EnumBlockHalf.BOTTOM), + Blocks.WOODEN_SLAB.getDefaultState().withProperty(BlockSlab.HALF, EnumBlockHalf.TOP), + wallMaterial, + Blocks.GLASS_PANE.getDefaultState(), + Blocks.OAK_DOOR.getDefaultState(), + WizardryBlocks.arcane_workbench.getDefaultState(), + Blocks.TORCH.getDefaultState(), + evilWizard ? Blocks.CHEST.getDefaultState() : Blocks.BOOKSHELF.getDefaultState() + }; + + Set blocksPlaced = new HashSet(); + + // == Foundations == + + // Fills in foundations. This is done first so the door always has something to be placed on. + boolean flag = true; + + // BlockPos is immutable, so I'm not sure if simply saying pos1 = pos will be sufficient. + BlockPos layerCentre = new BlockPos(origin); + + while(flag){ + + flag = false; + + for(BlockPos offset : foundationLayer){ + if(!world.isBlockNormalCube(layerCentre.add(offset), false)){ + world.setBlockState(layerCentre.add(offset), wallMaterial); + blocksPlaced.add(layerCentre.add(offset)); + // Keeps going as long as something was filled in. + flag = true; + } + } + + layerCentre = layerCentre.down(); + } + + // == Main Structure == + + // It is assumed that the width of the blueprint is the same all the way up, and that the layers are square. + int width = towerBlueprint[0].length-1; + + // x, y and z are the position the block is being put in. + // x1, y and z1 are the position in the blueprint which determines which block is being placed. + + int x1 = 0, z1 = 0; + + for(int y=0; y 3 || world.getBlockState(pos1).getBlock() instanceof BlockLiquid)){ + return false; + } + } + } + } + + return true; + } + + /** Array of relative positions of each block in a layer of foundations. */ + private static final BlockPos[] foundationLayer = new BlockPos[]{ + new BlockPos(-2, 0, -1), + new BlockPos(-2, 0, 0), + new BlockPos(-2, 0, 1), + new BlockPos(2, 0, -1), + new BlockPos(2, 0, 0), + new BlockPos(2, 0, 1), + new BlockPos(-1, 0, -2), + new BlockPos(0, 0, -2), + new BlockPos(1, 0, -2), + new BlockPos(-1, 0, 2), + new BlockPos(0, 0, 2), + new BlockPos(1, 0, 2), + }; + + /** + * 3D matrix of integers representing the different blocks which make up the wizard tower. The blocks corresponding + * to each integer are as follows (note that some blocks change depending on the biome): + *

    + * 0 Nothing (keep existing block)
    + * 1 Air (remove existing block)
    + * 2 Floor (planks)
    + * 3 Bookshelf
    + * 4 Roof (stained clay)
    + * 5 Floor slab (lower half)
    + * 6 Floor slab (upper half)
    + * 7 Wall (cobblestone by default)
    + * 8 Glass pane
    + * 9 Door (metadata is handled by the built-in vanilla method)
    + * 10 Arcane workbench
    + * 11 Torch (metadata is handled separately depending on adjacent blocks)
    + * 12 Chest (only generates if the wizard is evil, otherwise places a bookshelf instead) + */ + private static final int[][][] towerBlueprintSmall = { + // x is horizontal, z is vertical, y is layers + { + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 2, 2, 2, 0, 0, 0}, + {0, 0, 0, 2, 2, 2, 0, 0, 0}, + {0, 0, 0, 2, 2, 2, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 6, 5, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 9, 7, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 5, 1 ,1, 7, 0, 0}, + {0, 0, 7, 6, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 0, 7, 0, 0, 0}, + {0, 0, 0, 11,1, 11,0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 11,7, 0, 0}, + {0, 0, 7, 5, 6, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 6, 7, 0, 0}, + {0, 0, 7, 1, 1, 5, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 6, 5, 7, 0, 0}, + {0, 0, 7, 11,1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 5, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 2, 2, 2, 7, 0, 0}, + {0, 7, 1, 1, 1, 1, 2, 7, 0}, + {0, 7, 5, 2, 2, 6, 2, 7, 0}, + {0, 7, 2, 2, 2, 2, 2, 7, 0}, + {0, 0, 7, 2, 2, 2, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 3, 3, 3, 7, 0, 0}, + {0, 7, 1, 1, 1, 1, 3, 7, 0}, + {0, 7, 1, 1, 1, 1, 3, 7, 0}, + {0, 7, 1, 1, 10,1, 3, 7, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 7, 11,1, 11,7, 0, 0}, + {0, 7, 1, 1, 1, 1, 3, 7, 0}, + {0, 8, 1, 1, 1, 1, 3, 7, 0}, + {0, 7, 1, 1, 1, 1, 3, 7, 0}, + {0, 0, 7, 11,1, 11,7, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 4, 7, 7, 7, 4, 0, 0}, + {0, 4, 7, 1, 1, 1, 7, 4, 0}, + {4, 7, 1, 1, 1, 1, 3, 7, 4}, + {4, 7, 1, 1, 1, 1, 3, 7, 4}, + {4, 7, 1, 1, 1, 1, 12,7, 4}, + {0, 4, 7, 1, 1, 1, 7, 4, 0}, + {0, 0, 4, 7, 7, 7, 4, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 4, 1, 1, 1, 1, 1, 4, 0}, + {0, 4, 1, 1, 1, 1, 1, 4, 0}, + {0, 4, 1, 1, 1, 1, 1, 4, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 4, 1, 4, 0, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 0, 4, 1, 4, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 0, 4, 1, 4, 0, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 4, 1, 4, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + } + }; + + /** + * 3D matrix of integers representing the different blocks which make up the wizard tower. The blocks corresponding + * to each integer are as follows (note that some blocks change depending on the biome): + *

    + * 0 Nothing (keep existing block)
    + * 1 Air (remove existing block)
    + * 2 Floor (planks)
    + * 3 Bookshelf
    + * 4 Roof (stained clay)
    + * 5 Floor slab (lower half)
    + * 6 Floor slab (upper half)
    + * 7 Wall (cobblestone by default)
    + * 8 Glass pane
    + * 9 Door (metadata is handled by the built-in vanilla method)
    + * 10 Arcane workbench
    + * 11 Torch (metadata is handled separately depending on adjacent blocks)
    + * 12 Chest (only generates if the wizard is evil, otherwise places a bookshelf instead) + */ + private static final int[][][] towerBlueprintMedium = { + // x is horizontal, z is vertical, y is layers + { + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 2, 2, 2, 0, 0, 0}, + {0, 0, 0, 2, 2, 2, 0, 0, 0}, + {0, 0, 0, 2, 2, 2, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 6, 5, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 9, 7, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 5, 1 ,1, 7, 0, 0}, + {0, 0, 7, 6, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 0, 7, 0, 0, 0}, + {0, 0, 0, 11,1, 11,0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 11,7, 0, 0}, + {0, 0, 7, 5, 6, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 6, 7, 0, 0}, + {0, 0, 7, 1, 1, 5, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 6, 5, 7, 0, 0}, + {0, 0, 7, 11,1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 5, 1 ,1, 7, 0, 0}, + {0, 0, 7, 6, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 11,7, 0, 0}, + {0, 0, 7, 5, 6, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 6, 7, 0, 0}, + {0, 0, 7, 1, 1, 5, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 6, 5, 7, 0, 0}, + {0, 0, 7, 11,1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 5, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 2, 2, 2, 7, 0, 0}, + {0, 7, 1, 1, 1, 1, 2, 7, 0}, + {0, 7, 5, 2, 2, 6, 2, 7, 0}, + {0, 7, 2, 2, 2, 2, 2, 7, 0}, + {0, 0, 7, 2, 2, 2, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 3, 3, 3, 7, 0, 0}, + {0, 7, 1, 1, 1, 1, 3, 7, 0}, + {0, 7, 1, 1, 1, 1, 3, 7, 0}, + {0, 7, 1, 1, 10,1, 3, 7, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 7, 11,1, 11,7, 0, 0}, + {0, 7, 1, 1, 1, 1, 3, 7, 0}, + {0, 8, 1, 1, 1, 1, 3, 7, 0}, + {0, 7, 1, 1, 1, 1, 3, 7, 0}, + {0, 0, 7, 11,1, 11,7, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 4, 7, 7, 7, 4, 0, 0}, + {0, 4, 7, 1, 1, 1, 7, 4, 0}, + {4, 7, 1, 1, 1, 1, 3, 7, 4}, + {4, 7, 1, 1, 1, 1, 3, 7, 4}, + {4, 7, 1, 1, 1, 1, 12,7, 4}, + {0, 4, 7, 1, 1, 1, 7, 4, 0}, + {0, 0, 4, 7, 7, 7, 4, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 4, 1, 1, 1, 1, 1, 4, 0}, + {0, 4, 1, 1, 1, 1, 1, 4, 0}, + {0, 4, 1, 1, 1, 1, 1, 4, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 4, 1, 4, 0, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 0, 4, 1, 4, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 0, 4, 1, 4, 0, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 4, 1, 4, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + } + }; + + /** + * 3D matrix of integers representing the different blocks which make up the wizard tower. The blocks corresponding + * to each integer are as follows (note that some blocks change depending on the biome): + *

    + * 0 Nothing (keep existing block)
    + * 1 Air (remove existing block)
    + * 2 Floor (planks)
    + * 3 Bookshelf
    + * 4 Roof (stained clay)
    + * 5 Floor slab (lower half)
    + * 6 Floor slab (upper half)
    + * 7 Wall (cobblestone by default)
    + * 8 Glass pane
    + * 9 Door (metadata is handled by the built-in vanilla method)
    + * 10 Arcane workbench
    + * 11 Torch (metadata is handled separately depending on adjacent blocks)
    + * 12 Chest (only generates if the wizard is evil, otherwise places a bookshelf instead) + */ + private static final int[][][] towerBlueprintTall = { + // x is horizontal, z is vertical, y is layers + { + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 2, 2, 2, 0, 0, 0}, + {0, 0, 0, 2, 2, 2, 0, 0, 0}, + {0, 0, 0, 2, 2, 2, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 6, 5, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 9, 7, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 5, 1 ,1, 7, 0, 0}, + {0, 0, 7, 6, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 0, 7, 0, 0, 0}, + {0, 0, 0, 11,1, 11,0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 11,7, 0, 0}, + {0, 0, 7, 5, 6, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 6, 7, 0, 0}, + {0, 0, 7, 1, 1, 5, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 6, 5, 7, 0, 0}, + {0, 0, 7, 11,1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 5, 1 ,1, 7, 0, 0}, + {0, 0, 7, 6, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 11,7, 0, 0}, + {0, 0, 7, 5, 6, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 6, 7, 0, 0}, + {0, 0, 7, 1, 1, 5, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 6, 5, 7, 0, 0}, + {0, 0, 7, 11,1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 5, 1 ,1, 7, 0, 0}, + {0, 0, 7, 6, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 11,7, 0, 0}, + {0, 0, 7, 5, 6, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 6, 7, 0, 0}, + {0, 0, 7, 1, 1, 5, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 1, 6, 5, 7, 0, 0}, + {0, 0, 7, 11,1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 5, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 2, 2, 2, 7, 0, 0}, + {0, 7, 1, 1, 1, 1, 2, 7, 0}, + {0, 7, 5, 2, 2, 6, 2, 7, 0}, + {0, 7, 2, 2, 2, 2, 2, 7, 0}, + {0, 0, 7, 2, 2, 2, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 7, 3, 3, 3, 7, 0, 0}, + {0, 7, 1, 1, 1, 1, 3, 7, 0}, + {0, 7, 1, 1, 1, 1, 3, 7, 0}, + {0, 7, 1, 1, 10,1, 3, 7, 0}, + {0, 0, 7, 1, 1, 1, 7, 0, 0}, + {0, 0, 0, 7, 7, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 7, 11,1, 11,7, 0, 0}, + {0, 7, 1, 1, 1, 1, 3, 7, 0}, + {0, 8, 1, 1, 1, 1, 3, 7, 0}, + {0, 7, 1, 1, 1, 1, 3, 7, 0}, + {0, 0, 7, 11,1, 11,7, 0, 0}, + {0, 0, 0, 7, 8, 7, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 4, 7, 7, 7, 4, 0, 0}, + {0, 4, 7, 1, 1, 1, 7, 4, 0}, + {4, 7, 1, 1, 1, 1, 3, 7, 4}, + {4, 7, 1, 1, 1, 1, 3, 7, 4}, + {4, 7, 1, 1, 1, 1, 12,7, 4}, + {0, 4, 7, 1, 1, 1, 7, 4, 0}, + {0, 0, 4, 7, 7, 7, 4, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 4, 1, 1, 1, 1, 1, 4, 0}, + {0, 4, 1, 1, 1, 1, 1, 4, 0}, + {0, 4, 1, 1, 1, 1, 1, 4, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 4, 1, 4, 0, 0, 0}, + {0, 0, 4, 1, 1, 1, 4, 0, 0}, + {0, 0, 0, 4, 1, 4, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 0, 4, 1, 4, 0, 0, 0}, + {0, 0, 0, 4, 4, 4, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 4, 1, 4, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + } + }; + + /** + * 3D matrix of integers representing the different blocks which make up the wizard tower. The blocks corresponding + * to each integer are as follows (note that some blocks change depending on the biome): + *

    + * 0 Nothing (keep existing block)
    + * 1 Air (remove existing block)
    + * 2 Floor (planks)
    + * 3 Bookshelf
    + * 4 Roof (stained clay)
    + * 5 Floor slab (lower half)
    + * 6 Floor slab (upper half)
    + * 7 Wall (cobblestone by default)
    + * 8 Glass pane
    + * 9 Door (metadata is handled by the built-in vanilla method)
    + * 10 Arcane workbench
    + * 11 Torch (metadata is handled separately depending on adjacent blocks)
    + * 12 Chest (only generates if the wizard is evil, otherwise places a bookshelf instead) + */ + private static final int[][][] towerBlueprintDouble = { + // x is horizontal, z is vertical, y is layers + { + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 1, 6, 5, 7, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 9, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 5, 1 ,1, 7, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 6, 1, 1, 7, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 0, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 11,1, 11,0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 1, 1, 11,7, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 5, 6, 1, 7, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 1, 1, 6, 7, 7, 7, 0, 0}, + {0, 0, 0, 0, 7, 1, 1, 5, 7, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 1, 6, 5, 7, 7, 7, 0, 0}, + {0, 0, 0, 0, 7, 11,1, 1, 1, 1, 1, 7, 0}, + {0, 0, 0, 0, 7, 1, 1, 1, 7, 7, 7, 0, 0}, + {0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 5, 1 ,1, 7, 7, 8, 7, 0}, + {0, 0, 0, 0, 7, 6, 1, 1, 1, 1, 1, 8, 0}, + {0, 0, 0, 0, 7, 1, 1, 1, 7, 7, 8, 7, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 8, 7, 0, 4, 4, 4, 0}, + {0, 0, 0, 0, 7, 1, 1, 1, 7, 7, 7, 7, 4}, + {0, 0, 0, 0, 7, 1, 1, 11,7, 1, 1, 7, 4}, + {0, 0, 0, 0, 7, 5, 6, 1, 7, 7, 7, 7, 4}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 4, 4, 4, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 1, 1, 1, 7, 4, 4, 4, 0}, + {0, 0, 0, 0, 7, 1, 1, 6, 7, 4, 1, 4, 0}, + {0, 0, 0, 0, 7, 1, 1, 5, 7, 4, 4, 4, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 1, 6, 5, 7, 0, 4, 0, 0}, + {0, 0, 0, 0, 7, 11,1, 1, 7, 4, 1, 4, 0}, + {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 4, 0, 0}, + {0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 5, 1, 1, 7, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 4, 0, 0}, + {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 2, 2, 2, 7, 0, 0, 0, 0}, + {0, 0, 0, 7, 1, 1, 1, 1, 2, 7, 0, 0, 0}, + {0, 0, 0, 7, 5, 2, 2, 6, 2, 7, 4, 0, 0}, + {0, 0, 0, 7, 2, 2, 2, 2, 2, 7, 0, 0, 0}, + {0, 0, 0, 0, 7, 2, 2, 2, 7, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 3, 3, 3, 7, 0, 0, 0, 0}, + {0, 0, 0, 7, 1, 1, 1, 1, 3, 7, 0, 0, 0}, + {0, 0, 0, 7, 1, 1, 1, 1, 3, 7, 0, 0, 0}, + {0, 0, 0, 7, 1, 1, 10,1, 3, 7, 0, 0, 0}, + {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 7, 11,1, 11,7, 0, 0, 0, 0}, + {0, 0, 0, 7, 1, 1, 1, 1, 3, 7, 0, 0, 0}, + {0, 0, 0, 8, 1, 1, 1, 1, 3, 7, 0, 0, 0}, + {0, 0, 0, 7, 1, 1, 1, 1, 3, 7, 0, 0, 0}, + {0, 0, 0, 0, 7, 11,1, 11,7, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 7, 7, 7, 4, 0, 0, 0, 0}, + {0, 0, 0, 4, 7, 1, 1, 1, 7, 4, 0, 0, 0}, + {0, 0, 4, 7, 1, 1, 1, 1, 3, 7, 4, 0, 0}, + {0, 0, 4, 7, 1, 1, 1, 1, 3, 7, 4, 0, 0}, + {0, 0, 4, 7, 1, 1, 1, 1, 12,7, 4, 0, 0}, + {0, 0, 0, 4, 7, 1, 1, 1, 7, 4, 0, 0, 0}, + {0, 0, 0, 0, 4, 7, 7, 7, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0}, + {0, 0, 0, 4, 1, 1, 1, 1, 1, 4, 0, 0, 0}, + {0, 0, 0, 4, 1, 1, 1, 1, 1, 4, 0, 0, 0}, + {0, 0, 0, 4, 1, 1, 1, 1, 1, 4, 0, 0, 0}, + {0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 4, 1, 4, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 4, 1, 4, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 4, 1, 4, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 4, 1, 4, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + },{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + } + }; + + +} diff --git a/src/main/java/electroblob/wizardry/block/BlockArcaneWorkbench.java b/src/main/java/electroblob/wizardry/block/BlockArcaneWorkbench.java new file mode 100644 index 00000000..4a0c3f8f --- /dev/null +++ b/src/main/java/electroblob/wizardry/block/BlockArcaneWorkbench.java @@ -0,0 +1,120 @@ +package electroblob.wizardry.block; + +import java.util.Random; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryGuiHandler; +import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; +import net.minecraft.block.BlockContainer; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumBlockRenderType; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; + +public class BlockArcaneWorkbench extends BlockContainer { + + private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 0.75D, 1.0D); + + public BlockArcaneWorkbench(){ + super(Material.ROCK); + this.setLightLevel(0.8f); + } + + @Override + public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){ + return AABB; + } + + @Override + public TileEntity createNewTileEntity(World world, int metadata) { + return new TileEntityArcaneWorkbench(); + } + + @Override + public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos) { + return false; + } + + @Override + public EnumBlockRenderType getRenderType(IBlockState state) { + return EnumBlockRenderType.MODEL; + } + + @Override + public boolean isOpaqueCube(IBlockState state) { + return false; + } + + @Override + public boolean onBlockActivated(World world, BlockPos pos, IBlockState block, EntityPlayer player, + EnumHand hand, ItemStack heldItem, EnumFacing side, float hitX, float hitY, float hitZ){ + + TileEntity tileEntity = world.getTileEntity(pos); + + if(tileEntity == null || player.isSneaking()){ + return false; + } + + player.openGui(Wizardry.instance, WizardryGuiHandler.ARCANE_WORKBENCH, world, pos.getX(), pos.getY(), pos.getZ()); + return true; + } + + @Override + public void breakBlock(World world, BlockPos pos, IBlockState block) + { + Random random = new Random(); + TileEntityArcaneWorkbench tileentityarcaneworkbench = (TileEntityArcaneWorkbench)world.getTileEntity(pos); + + if (tileentityarcaneworkbench != null) + { + for (int j1 = 0; j1 < tileentityarcaneworkbench.getSizeInventory(); ++j1) + { + ItemStack itemstack = tileentityarcaneworkbench.getStackInSlot(j1); + + if (itemstack != null) + { + float f = random.nextFloat() * 0.8F + 0.1F; + float f1 = random.nextFloat() * 0.8F + 0.1F; + EntityItem entityitem; + + for (float f2 = random.nextFloat() * 0.8F + 0.1F; itemstack.stackSize > 0; world.spawnEntityInWorld(entityitem)) + { + int k1 = random.nextInt(21) + 10; + + if (k1 > itemstack.stackSize) + { + k1 = itemstack.stackSize; + } + + itemstack.stackSize -= k1; + entityitem = new EntityItem(world, (double)((float)pos.getX() + f), (double)((float)pos.getY() + f1), (double)((float)pos.getZ() + f2), new ItemStack(itemstack.getItem(), k1, itemstack.getItemDamage())); + float f3 = 0.05F; + entityitem.motionX = (double)((float)random.nextGaussian() * f3); + entityitem.motionY = (double)((float)random.nextGaussian() * f3 + 0.2F); + entityitem.motionZ = (double)((float)random.nextGaussian() * f3); + + if (itemstack.hasTagCompound()) + { + entityitem.getEntityItem().setTagCompound(((NBTTagCompound)itemstack.getTagCompound().copy())); + } + } + } + } + + //par1World.func_96440_m(par2, par3, par4, block); + } + + super.breakBlock(world, pos, block); + } + +} diff --git a/src/main/java/electroblob/wizardry/block/BlockCrystalFlower.java b/src/main/java/electroblob/wizardry/block/BlockCrystalFlower.java new file mode 100644 index 00000000..13daca12 --- /dev/null +++ b/src/main/java/electroblob/wizardry/block/BlockCrystalFlower.java @@ -0,0 +1,46 @@ +package electroblob.wizardry.block; + +import java.util.Random; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.block.BlockBush; +import net.minecraft.block.SoundType; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.EnumPlantType; + +// Extending BlockBush allows me to remove nearly everything from this class. +public class BlockCrystalFlower extends BlockBush { + + private static final AxisAlignedBB AABB = new AxisAlignedBB(0.5F - 0.2f, 0.0F, 0.5F - 0.2f, 0.5F + 0.2f, 0.2f * 3.0F, 0.5F + 0.2f); + + public BlockCrystalFlower(Material par2Material) { + super(par2Material); + this.setLightLevel(0.5f); + this.setTickRandomly(true); + this.setSoundType(SoundType.PLANT); + } + + @Override + public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos) + { + return AABB; + } + + @Override + public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random random){ + if(world.isRemote && random.nextBoolean()){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, pos.getX()+random.nextDouble(), pos.getY()+random.nextDouble()/2+0.5, pos.getZ()+random.nextDouble(), 0d, 0.01, 0d, 20 + random.nextInt(10), 0.5f + (random.nextFloat()/2), 0.5f + (random.nextFloat()/2), 0.5f + (random.nextFloat()/2)); + } + } + + @Override + public EnumPlantType getPlantType(IBlockAccess world, BlockPos pos) { + return EnumPlantType.Plains; + } +} diff --git a/src/main/java/electroblob/wizardry/block/BlockCrystalOre.java b/src/main/java/electroblob/wizardry/block/BlockCrystalOre.java new file mode 100644 index 00000000..dc594dc0 --- /dev/null +++ b/src/main/java/electroblob/wizardry/block/BlockCrystalOre.java @@ -0,0 +1,35 @@ +package electroblob.wizardry.block; + +import java.util.Random; + +import electroblob.wizardry.registry.WizardryItems; +import net.minecraft.block.Block; +import net.minecraft.block.SoundType; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.Item; + +public class BlockCrystalOre extends Block { + + public BlockCrystalOre(Material material) { + super(material); + this.setSoundType(SoundType.STONE); + setResistance(5.0F); + setHarvestLevel("pickaxe", 2); + } + + @Override + public int quantityDropped(IBlockState state, int fortune, Random random) + { + if(fortune > 0){ + return random.nextInt(2) + 1 + random.nextInt(fortune); + }else{ + return random.nextInt(2) + 1; + } + } + + @Override + public Item getItemDropped(IBlockState state, Random random, int fortune){ + return WizardryItems.magic_crystal; + } +} diff --git a/src/main/java/electroblob/wizardry/block/BlockMagicLight.java b/src/main/java/electroblob/wizardry/block/BlockMagicLight.java new file mode 100644 index 00000000..c3ef8499 --- /dev/null +++ b/src/main/java/electroblob/wizardry/block/BlockMagicLight.java @@ -0,0 +1,58 @@ +package electroblob.wizardry.block; + +import electroblob.wizardry.tileentity.TileEntityMagicLight; +import net.minecraft.block.BlockContainer; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumBlockRenderType; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; + +public class BlockMagicLight extends BlockContainer { + + private static final AxisAlignedBB AABB = new AxisAlignedBB(0, 0, 0, 0, 0, 0); + + public BlockMagicLight(Material par2Material) { + super(par2Material); + this.setLightLevel(1.0f); + } + + @Override + public AxisAlignedBB getCollisionBoundingBox(IBlockState blockState, World worldIn, BlockPos pos){ + // The other two bounding box methods in Block aren't nullable, so this is the only one that can return NULL_AABB. + return NULL_AABB; + } + + @Override + public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){ + return AABB; + } + + @Override + public boolean isCollidable(){ + return false; + } + + @Override + public TileEntity createNewTileEntity(World world, int metadata) { + return new TileEntityMagicLight(600); + } + + @Override + public boolean isOpaqueCube(IBlockState state) { + return false; + } + + @Override + public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos) { + return false; + } + + @Override + public EnumBlockRenderType getRenderType(IBlockState state) { + return EnumBlockRenderType.ENTITYBLOCK_ANIMATED; + } +} diff --git a/src/main/java/electroblob/wizardry/block/BlockSnare.java b/src/main/java/electroblob/wizardry/block/BlockSnare.java new file mode 100644 index 00000000..d020a580 --- /dev/null +++ b/src/main/java/electroblob/wizardry/block/BlockSnare.java @@ -0,0 +1,110 @@ +package electroblob.wizardry.block; + +import java.util.Random; + +import electroblob.wizardry.tileentity.TileEntityPlayerSave; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.block.Block; +import net.minecraft.block.BlockContainer; +import net.minecraft.block.SoundType; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.MobEffects; +import net.minecraft.item.Item; +import net.minecraft.potion.PotionEffect; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.EnumBlockRenderType; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +// TODO: Apparently you shouldn't extend BlockContainer. I feel like BlockArcaneWorkbench should, but what about the rest? +public class BlockSnare extends BlockContainer { + + private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0f, 0.0f, 0.0f, 1.0f, 0.0625f, 1.0f); + + public BlockSnare(Material par2Material){ + super(par2Material); + this.setSoundType(SoundType.PLANT); + } + + @Override + public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){ + return AABB; + } + + @Override + public AxisAlignedBB getCollisionBoundingBox(IBlockState blockState, World worldIn, BlockPos pos){ + return NULL_AABB; + } + + @Override + public boolean hasTileEntity(IBlockState state){ + return true; + } + + @Override + public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity) { + + if(!world.isRemote && entity instanceof EntityLivingBase){ + if(world.getTileEntity(pos) instanceof TileEntityPlayerSave){ + + TileEntityPlayerSave tileentity = (TileEntityPlayerSave)world.getTileEntity(pos); + + if(WizardryUtilities.isValidTarget(tileentity.getCaster(), entity)){ + ((EntityLivingBase)entity).attackEntityFrom(MagicDamage.causeDirectMagicDamage(tileentity.getCaster(), DamageType.MAGIC), 6); + ((EntityLivingBase)entity).addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, 100, 2)); + + world.destroyBlock(pos, false); + } + } + } + } + + // The similarly named onNeighborChange method does NOT do the same thing. + @SuppressWarnings("deprecation") + @Override + public void neighborChanged(IBlockState state, World world, BlockPos pos, Block blockIn){ + super.neighborChanged(state, world, pos, blockIn); + if(!world.isSideSolid(pos.down(), EnumFacing.UP, false)){ + world.setBlockToAir(pos); + } + } + + @Override + public BlockRenderLayer getBlockLayer(){ + return BlockRenderLayer.CUTOUT; + } + + @Override + public EnumBlockRenderType getRenderType(IBlockState state){ + return EnumBlockRenderType.MODEL; + } + + @Override + public boolean isOpaqueCube(IBlockState state){ + return false; + } + + @Override + public boolean isFullCube(IBlockState state){ + return false; + } + + @Override + public Item getItemDropped(IBlockState state, Random rand, int fortune){ + return null; + } + + @Override + public TileEntity createNewTileEntity(World world, int metadata) { + return new TileEntityPlayerSave(); + } + +} diff --git a/src/main/java/electroblob/wizardry/block/BlockSpectral.java b/src/main/java/electroblob/wizardry/block/BlockSpectral.java new file mode 100644 index 00000000..e151e845 --- /dev/null +++ b/src/main/java/electroblob/wizardry/block/BlockSpectral.java @@ -0,0 +1,94 @@ +package electroblob.wizardry.block; + +import java.util.Random; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.tileentity.TileEntityTimer; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.block.Block; +import net.minecraft.block.BlockContainer; +import net.minecraft.block.SoundType; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.EnumBlockRenderType; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +// For future reference - extend BlockContainer whenever possible because it has methods for removing tile entities on +// block break. +public class BlockSpectral extends BlockContainer { + + public BlockSpectral(Material material) { + super(material); + this.setSoundType(SoundType.GLASS); + } + + // Replaces getRenderBlockPass + @Override + public BlockRenderLayer getBlockLayer(){ + return BlockRenderLayer.TRANSLUCENT; + } + + @Override + public EnumBlockRenderType getRenderType(IBlockState state) { + return EnumBlockRenderType.MODEL; + } + + // Apparently it's OK to override this, despite it being deprecated. More importantly, it being deprecated is not + // Forge's doing, rather it is Mojang themselves misusing the @Deprecated annotation to mean 'internal, don't call'. + @Override + public boolean isOpaqueCube(IBlockState state){ + return false; + } + + @Override + public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos) { + return false; + } + + @Override + public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random random) { + // Middle of block + Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, world, pos.getX()+random.nextDouble(), pos.getY()+random.nextDouble(), pos.getZ()+random.nextDouble(), 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)), + 0.4f + random.nextFloat()*0.2f, 0.6f + random.nextFloat()*0.4f, 0.6f + random.nextFloat()*0.4f); + // Top surface + Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, world, pos.getX()+random.nextDouble(), pos.getY()+1, pos.getZ()+random.nextDouble(), 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)), + 0.4f + random.nextFloat()*0.2f, 0.6f + random.nextFloat()*0.4f, 0.6f + random.nextFloat()*0.4f); + Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, world, pos.getX()+random.nextDouble(), pos.getY()+1, pos.getZ()+random.nextDouble(), 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)), + 0.4f + random.nextFloat()*0.2f, 0.6f + random.nextFloat()*0.4f, 0.6f + random.nextFloat()*0.4f); + } + + // Overriden to make the block always look full brightness despite not emitting full light. + @Override + public int getPackedLightmapCoords(IBlockState state, IBlockAccess source, BlockPos pos) { + return 15; + } + + @Override + public TileEntity createNewTileEntity(World world, int metadata) { + return new TileEntityTimer(1200); + } + + @Override + public int quantityDropped(Random par1Random){ + return 0; + } + + @SuppressWarnings("deprecation") + @SideOnly(Side.CLIENT) + @Override + public boolean shouldSideBeRendered(IBlockState blockState, IBlockAccess blockAccess, BlockPos pos, EnumFacing side){ + + IBlockState iblockstate = blockAccess.getBlockState(pos.offset(side)); + Block block = iblockstate.getBlock(); + + return block == this ? false : super.shouldSideBeRendered(blockState, blockAccess, pos, side); + } + +} diff --git a/src/main/java/electroblob/wizardry/block/BlockStatue.java b/src/main/java/electroblob/wizardry/block/BlockStatue.java new file mode 100644 index 00000000..3b3e9b5e --- /dev/null +++ b/src/main/java/electroblob/wizardry/block/BlockStatue.java @@ -0,0 +1,146 @@ +package electroblob.wizardry.block; + +import java.util.Random; + +import electroblob.wizardry.spell.Petrify; +import electroblob.wizardry.tileentity.TileEntityStatue; +import net.minecraft.block.Block; +import net.minecraft.block.BlockContainer; +import net.minecraft.block.SoundType; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.EnumBlockRenderType; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class BlockStatue extends BlockContainer { + + private boolean isIce; + + public BlockStatue(Material material) { + super(material); + this.isIce = material == Material.ICE; + if(this.isIce){ + this.slipperiness = 0.98F; + this.setSoundType(SoundType.GLASS); + } + } + + @Override + public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos){ + // Not a good idea to call getBlockBoundsMinX() or whatever from in here, since this method changes those! + if(!this.isIce){ + + if(world.getTileEntity(pos) instanceof TileEntityStatue){ + + TileEntityStatue statue = (TileEntityStatue)world.getTileEntity(pos); + + if(statue.creature != null){ + + // Block bounds are set to match the width and height of the entity, clamped to within 1 block. + return new AxisAlignedBB((float)Math.max(0.5 - statue.creature.width/2, 0), 0, + (float)Math.max(0.5 - statue.creature.width/2, 0), + (float)Math.min(0.5 + statue.creature.width/2, 1), + // This checks if the block is the top one and if so reduces its height so the top lines up with + // the top of the entity model. + statue.position == statue.parts ? (float)Math.min(statue.creature.height - statue.parts + 1, 1) : 1, + (float)Math.min(0.5 + statue.creature.width/2, 1)); + } + } + } + + return FULL_BLOCK_AABB; + } + + // getCollisionBoundingBox eventually calls getBoundingBox anyway, and since I want the collision box and the block + // outline to be the same here, I've removed that getCollisionBoundingBox entirely. + + // The number of these methods is quite simply ridiculous. This one seems to be for placement logic and block + // connections (fences, glass panes, etc.)... + @Override public boolean isFullCube(IBlockState state) { return false; } + // ...this one isn't used much but has something to do with redstone... + @Override public boolean isBlockNormalCube(IBlockState state) { return false; } + // ... this one is for most other game logic... + @Override public boolean isNormalCube(IBlockState state) { return false; } + // Forge version of the above method. I still need to override both though because vanilla uses the other one. + @Override public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos) { return false; } + // ... and this one is for rendering. + @Override public boolean isOpaqueCube(IBlockState state){ return false; } + + @Override + public BlockRenderLayer getBlockLayer(){ + return this.isIce ? BlockRenderLayer.TRANSLUCENT : BlockRenderLayer.SOLID; + } + + @Override + public EnumBlockRenderType getRenderType(IBlockState state){ + return this.isIce ? EnumBlockRenderType.MODEL : EnumBlockRenderType.ENTITYBLOCK_ANIMATED; + } + + @Override + public TileEntity createNewTileEntity(World world, int metadata) { + return new TileEntityStatue(this.isIce); + } + + @Override + public int quantityDropped(Random random){ + return 0; + } + + @Override + public void breakBlock(World world, BlockPos pos, IBlockState state){ + + if(!world.isRemote){ + + TileEntityStatue tileentity = (TileEntityStatue)world.getTileEntity(pos); + + if(tileentity != null){ + if(tileentity.parts == 2){ + if(tileentity.position == 2){ + world.destroyBlock(pos.down(), false); + }else{ + world.destroyBlock(pos.up(), false); + } + }else if(tileentity.parts == 3){ + if(tileentity.position == 3){ + world.destroyBlock(pos.down(), false); + world.destroyBlock(pos.down(2), false); + }else if(tileentity.position == 2){ + world.destroyBlock(pos.down(), false); + world.destroyBlock(pos.up(), false); + }else{ + world.destroyBlock(pos.up(), false); + world.destroyBlock(pos.up(2), false); + } + } + } + + // This is only when position == 1 because world.destroyBlock calls this function for the other blocks. + if(tileentity != null && tileentity.position == 1 && tileentity.creature != null){ + tileentity.creature.getEntityData().removeTag(Petrify.NBT_KEY); + tileentity.creature.isDead = false; + world.spawnEntityInWorld(tileentity.creature); + } + } + + super.breakBlock(world, pos, state); + } + + @SuppressWarnings("deprecation") + @SideOnly(Side.CLIENT) + @Override + public boolean shouldSideBeRendered(IBlockState blockState, IBlockAccess blockAccess, BlockPos pos, EnumFacing side){ + + IBlockState iblockstate = blockAccess.getBlockState(pos.offset(side)); + Block block = iblockstate.getBlock(); + + return this.isIce && block == this ? false : super.shouldSideBeRendered(blockState, blockAccess, pos, side); + } +} diff --git a/src/main/java/electroblob/wizardry/block/BlockTransportationStone.java b/src/main/java/electroblob/wizardry/block/BlockTransportationStone.java new file mode 100644 index 00000000..681d5266 --- /dev/null +++ b/src/main/java/electroblob/wizardry/block/BlockTransportationStone.java @@ -0,0 +1,115 @@ +package electroblob.wizardry.block; + +import java.util.Random; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardryBlocks; +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; + +public class BlockTransportationStone extends Block { + + private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0625f*5, 0, 0.0625f*5, 0.0625f*11, 0.0625f*6, 0.0625f*11); + + public BlockTransportationStone(Material material){ + super(material); + this.setTickRandomly(true); + } + + @Override + public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){ + return AABB; + } + + // The number of these methods is quite simply ridiculous. This one seems to be for placement logic and block + // connections (fences, glass panes, etc.)... + @Override public boolean isFullCube(IBlockState state) { return false; } + // ...this one isn't used much but has something to do with redstone... + @Override public boolean isBlockNormalCube(IBlockState state) { return false; } + // ... this one is for most other game logic... + @Override public boolean isNormalCube(IBlockState state) { return false; } + // Forge version of the above method. I still need to override both though because vanilla uses the other one. + @Override public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos) { return false; } + // ... and this one is for rendering. + @Override public boolean isOpaqueCube(IBlockState state){ return false; } + + @Override + public void onNeighborChange(IBlockAccess world, BlockPos pos, BlockPos neighbor){ + + super.onNeighborChange(world, pos, neighbor); + + if(!world.isSideSolid(pos.down(), EnumFacing.UP, false) && world instanceof World){ + this.dropBlockAsItem((World) world, pos, world.getBlockState(pos), 0); + ((World)world).setBlockToAir(pos); + } + } + + @Override + public void updateTick(World world, BlockPos pos, IBlockState state, Random random) { + + if(!world.isSideSolid(pos.down(), EnumFacing.UP)){ + this.dropBlockAsItem(world, pos, world.getBlockState(pos), 0); + world.setBlockToAir(pos); + } + } + + @Override + public boolean canPlaceBlockAt(World world, BlockPos pos){ + return super.canPlaceBlockAt(world, pos) && world.isSideSolid(pos.down(), EnumFacing.UP); + } + + @Override + public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, + EnumHand hand, ItemStack stack, EnumFacing side, float hitX, float hitY, float hitZ) { + + if(stack != null && stack.getItem() instanceof ItemWand){ + if(WizardData.get(player) != null){ + + WizardData data = WizardData.get(player); + + for(int x=-1; x<=1; x++){ + for(int z=-1; z<=1; z++){ + BlockPos pos1 = pos.add(x, 0, z); + if(testForCircle(world, pos1)){ + data.setStoneCircleLocation(pos1, world.provider.getDimension()); + if(!world.isRemote) player.addChatMessage(new TextComponentTranslation("tile.wizardry:transportation_stone.confirm", Spells.transportation.getNameForTranslationFormatted())); + return true; + } + } + } + + if(!world.isRemote) player.addChatMessage(new TextComponentTranslation("tile.wizardry:transportation_stone.invalid")); + return true; + } + } + return false; + } + + /** Returns whether the specified location is surrounded by a complete cicle of 8 transportation stones. */ + public static boolean testForCircle(World world, BlockPos pos){ + + if(world.getBlockState(pos).getMaterial().blocksMovement()) return false; + + for(int x=-1; x<=1; x++){ + for(int z=-1; z<=1; z++){ + if(world.getBlockState(pos.add(x, 0, z)).getBlock() != WizardryBlocks.transportation_stone){ + if(x != 0 || z != 0) return false; + } + } + } + + return true; + } +} diff --git a/src/main/java/electroblob/wizardry/block/BlockVanishingCobweb.java b/src/main/java/electroblob/wizardry/block/BlockVanishingCobweb.java new file mode 100644 index 00000000..3dcc77cd --- /dev/null +++ b/src/main/java/electroblob/wizardry/block/BlockVanishingCobweb.java @@ -0,0 +1,70 @@ +package electroblob.wizardry.block; + +import java.util.Random; + +import electroblob.wizardry.tileentity.TileEntityTimer; +import net.minecraft.block.BlockContainer; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.EnumBlockRenderType; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +// For future reference - extend BlockContainer whenever possible because it has methods for removing tile entities on block break. +public class BlockVanishingCobweb extends BlockContainer { + + public BlockVanishingCobweb(Material material) { + super(material); + } + + @SideOnly(Side.CLIENT) + public BlockRenderLayer getBlockLayer() + { + return BlockRenderLayer.CUTOUT; + } + + @Override + public EnumBlockRenderType getRenderType(IBlockState state){ + return EnumBlockRenderType.MODEL; + } + + @Override + public boolean isOpaqueCube(IBlockState state) + { + return false; + } + + @Override + public AxisAlignedBB getCollisionBoundingBox(IBlockState blockState, World worldIn, BlockPos pos) + { + return NULL_AABB; + } + + @Override + public boolean isFullCube(IBlockState state) + { + return false; + } + + @Override + public TileEntity createNewTileEntity(World world, int metadata) { + return new TileEntityTimer(400); + } + + @Override + public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity){ + entity.setInWeb(); + } + + @Override + public int quantityDropped(Random par1Random){ + return 0; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java new file mode 100644 index 00000000..dba2813d --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -0,0 +1,579 @@ +package electroblob.wizardry.client; + +import java.lang.ref.WeakReference; +import java.util.HashMap; + +import org.lwjgl.input.Keyboard; + +import electroblob.wizardry.CommonProxy; +import electroblob.wizardry.SpellGlyphData; +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.model.ModelWizardArmour; +import electroblob.wizardry.client.particle.ParticleBlizzard; +import electroblob.wizardry.client.particle.ParticleDarkMagic; +import electroblob.wizardry.client.particle.ParticleDust; +import electroblob.wizardry.client.particle.ParticleGiantBubble; +import electroblob.wizardry.client.particle.ParticleIce; +import electroblob.wizardry.client.particle.ParticleLeaf; +import electroblob.wizardry.client.particle.ParticleMagicFlame; +import electroblob.wizardry.client.particle.ParticlePath; +import electroblob.wizardry.client.particle.ParticleRotatingSparkle; +import electroblob.wizardry.client.particle.ParticleSnow; +import electroblob.wizardry.client.particle.ParticleSpark; +import electroblob.wizardry.client.particle.ParticleSparkle; +import electroblob.wizardry.client.particle.ParticleTornado; +import electroblob.wizardry.client.renderer.LayerStone; +import electroblob.wizardry.client.renderer.RenderArc; +import electroblob.wizardry.client.renderer.RenderArcaneWorkbench; +import electroblob.wizardry.client.renderer.RenderBlackHole; +import electroblob.wizardry.client.renderer.RenderBlank; +import electroblob.wizardry.client.renderer.RenderBubble; +import electroblob.wizardry.client.renderer.RenderDecay; +import electroblob.wizardry.client.renderer.RenderDecoy; +import electroblob.wizardry.client.renderer.RenderEvilWizard; +import electroblob.wizardry.client.renderer.RenderFireRing; +import electroblob.wizardry.client.renderer.RenderForceArrow; +import electroblob.wizardry.client.renderer.RenderHammer; +import electroblob.wizardry.client.renderer.RenderIceGiant; +import electroblob.wizardry.client.renderer.RenderIceSpike; +import electroblob.wizardry.client.renderer.RenderLightningDisc; +import electroblob.wizardry.client.renderer.RenderLightningPulse; +import electroblob.wizardry.client.renderer.RenderMagicArrow; +import electroblob.wizardry.client.renderer.RenderMagicLight; +import electroblob.wizardry.client.renderer.RenderPhoenix; +import electroblob.wizardry.client.renderer.RenderProjectile; +import electroblob.wizardry.client.renderer.RenderSigil; +import electroblob.wizardry.client.renderer.RenderSpiritHorse; +import electroblob.wizardry.client.renderer.RenderSpiritWolf; +import electroblob.wizardry.client.renderer.RenderStatue; +import electroblob.wizardry.client.renderer.RenderWizard; +import electroblob.wizardry.entity.EntityArc; +import electroblob.wizardry.entity.EntityShield; +import electroblob.wizardry.entity.construct.EntityArrowRain; +import electroblob.wizardry.entity.construct.EntityBlackHole; +import electroblob.wizardry.entity.construct.EntityBlizzard; +import electroblob.wizardry.entity.construct.EntityBubble; +import electroblob.wizardry.entity.construct.EntityDecay; +import electroblob.wizardry.entity.construct.EntityEarthquake; +import electroblob.wizardry.entity.construct.EntityFireRing; +import electroblob.wizardry.entity.construct.EntityFireSigil; +import electroblob.wizardry.entity.construct.EntityForcefield; +import electroblob.wizardry.entity.construct.EntityFrostSigil; +import electroblob.wizardry.entity.construct.EntityHailstorm; +import electroblob.wizardry.entity.construct.EntityHammer; +import electroblob.wizardry.entity.construct.EntityHealAura; +import electroblob.wizardry.entity.construct.EntityIceSpike; +import electroblob.wizardry.entity.construct.EntityLightningPulse; +import electroblob.wizardry.entity.construct.EntityLightningSigil; +import electroblob.wizardry.entity.construct.EntityTornado; +import electroblob.wizardry.entity.living.EntityDecoy; +import electroblob.wizardry.entity.living.EntityEvilWizard; +import electroblob.wizardry.entity.living.EntityIceGiant; +import electroblob.wizardry.entity.living.EntityIceWraith; +import electroblob.wizardry.entity.living.EntityLightningWraith; +import electroblob.wizardry.entity.living.EntityPhoenix; +import electroblob.wizardry.entity.living.EntityShadowWraith; +import electroblob.wizardry.entity.living.EntitySpiritHorse; +import electroblob.wizardry.entity.living.EntitySpiritWolf; +import electroblob.wizardry.entity.living.EntityStormElemental; +import electroblob.wizardry.entity.living.EntityWizard; +import electroblob.wizardry.entity.living.ISpellCaster; +import electroblob.wizardry.entity.living.ISummonedCreature; +import electroblob.wizardry.entity.projectile.EntityDarknessOrb; +import electroblob.wizardry.entity.projectile.EntityDart; +import electroblob.wizardry.entity.projectile.EntityFirebolt; +import electroblob.wizardry.entity.projectile.EntityFirebomb; +import electroblob.wizardry.entity.projectile.EntityForceArrow; +import electroblob.wizardry.entity.projectile.EntityForceOrb; +import electroblob.wizardry.entity.projectile.EntityIceCharge; +import electroblob.wizardry.entity.projectile.EntityIceLance; +import electroblob.wizardry.entity.projectile.EntityIceShard; +import electroblob.wizardry.entity.projectile.EntityLightningArrow; +import electroblob.wizardry.entity.projectile.EntityLightningDisc; +import electroblob.wizardry.entity.projectile.EntityMagicMissile; +import electroblob.wizardry.entity.projectile.EntityPoisonBomb; +import electroblob.wizardry.entity.projectile.EntitySmokeBomb; +import electroblob.wizardry.entity.projectile.EntitySpark; +import electroblob.wizardry.entity.projectile.EntitySparkBomb; +import electroblob.wizardry.entity.projectile.EntityThunderbolt; +import electroblob.wizardry.item.ItemScroll; +import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.packet.PacketCastContinuousSpell; +import electroblob.wizardry.packet.PacketCastSpell; +import electroblob.wizardry.packet.PacketNPCCastSpell; +import electroblob.wizardry.packet.PacketPlayerSync.Message; +import electroblob.wizardry.packet.PacketTransportation; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.spell.Clairvoyance; +import electroblob.wizardry.spell.None; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; +import electroblob.wizardry.tileentity.TileEntityMagicLight; +import electroblob.wizardry.tileentity.TileEntityStatue; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WandHelper; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.model.ModelBiped; +import net.minecraft.client.renderer.entity.RenderBlaze; +import net.minecraft.client.resources.I18n; +import net.minecraft.client.settings.KeyBinding; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityBlaze; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundEvent; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.World; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.config.Property; +import net.minecraftforge.fml.client.config.GuiConfigEntries.NumberSliderEntry; +import net.minecraftforge.fml.client.registry.ClientRegistry; +import net.minecraftforge.fml.client.registry.RenderingRegistry; + +/** + * The client proxy for wizardry. + * @author Electroblob + * @since Wizardry 1.0 + */ +public class ClientProxy extends CommonProxy { + + /** Static instance of the mixed font renderer */ + public static MixedFontRenderer mixedFontRenderer; + + // Key Bindings + public static final KeyBinding NEXT_SPELL = new KeyBinding("key.wizardry:next_spell", Keyboard.KEY_N, "key.categories.wizardry"); + public static final KeyBinding PREVIOUS_SPELL = new KeyBinding("key.wizardry:previous_spell", Keyboard.KEY_B, "key.categories.wizardry"); + + // Armour Model + public static final ModelBiped WIZARD_ARMOUR_MODEL = new ModelWizardArmour(0.75f); + + // SECTION Registry + // =============================================================================================================== + + @Override + public ModelBiped getWizardArmourModel(){ + return WIZARD_ARMOUR_MODEL; + } + + @Override + public void registerKeyBindings(){ + ClientRegistry.registerKeyBinding(NEXT_SPELL); + ClientRegistry.registerKeyBinding(PREVIOUS_SPELL); + } + + @Override + public void registerSpellHUD(){ + MinecraftForge.EVENT_BUS.register(new GuiSpellDisplay(Minecraft.getMinecraft())); + } + + @Override + public void initMixedFontRenderer(){ + mixedFontRenderer = new MixedFontRenderer(Minecraft.getMinecraft().gameSettings, + new ResourceLocation("textures/font/ascii.png"), Minecraft.getMinecraft().renderEngine, false); + } + + // SECTION Misc + // =============================================================================================================== + + @Override + public void setToNumberSliderEntry(Property property) { + property.setConfigEntryClass(NumberSliderEntry.class); + } + + @Override + public World getTheWorld() { + return Minecraft.getMinecraft().theWorld; + } + + @Override + public void playMovingSound(Entity entity, SoundEvent sound, float volume, float pitch, boolean repeat){ + Minecraft.getMinecraft().getSoundHandler().playSound(new MovingSoundEntity(entity, sound, volume, pitch, repeat)); + } + + // SECTION Items + // =============================================================================================================== + + @Override + public FontRenderer getFontRenderer(ItemStack stack){ + + Spell spell = Spells.none; + + if(stack.getItem() instanceof ItemWand){ + spell = WandHelper.getCurrentSpell(stack); + }else if(stack.getItem() instanceof ItemSpellBook || stack.getItem() instanceof ItemScroll){ + spell = Spell.get(stack.getItemDamage()); + } + + if(Minecraft.getMinecraft().thePlayer != null && Wizardry.settings.discoveryMode + && WizardData.get(Minecraft.getMinecraft().thePlayer) != null + && !Minecraft.getMinecraft().thePlayer.capabilities.isCreativeMode + && !WizardData.get(Minecraft.getMinecraft().thePlayer).hasSpellBeenDiscovered(spell)){ + return mixedFontRenderer; + } + + return null; + } + + @Override + public String getScrollDisplayName(ItemStack scroll){ + + // Displays [Empty slot] if spell is continuous. + Spell spell = Spell.get(scroll.getItemDamage()); + if(spell.isContinuous) spell = Spells.none; + + EntityPlayer player = Minecraft.getMinecraft().thePlayer; + + boolean discovered = true; + // It seems that this method is called when the world is loading, before thePlayer has been initialised. + // If the player is null, the spell is assumed to be discovered. + if(player != null && Wizardry.settings.discoveryMode && !player.capabilities.isCreativeMode + && WizardData.get(player) != null && !WizardData.get(player).hasSpellBeenDiscovered(spell)){ + discovered = false; + } + + if(discovered){ + return I18n.format("item.wizardry:scroll.name", spell.getDisplayName()).trim(); + }else{ + return I18n.format("item.wizardry:scroll.undiscovered.name", "#" + SpellGlyphData.getGlyphName(spell, player.worldObj) + "#").trim(); + } + } + + @Override + public double getConjuredBowDurability(ItemStack stack){ + EntityPlayer player = Minecraft.getMinecraft().thePlayer; + if(player.getActiveItemStack() == stack){ + return (double)(stack.getItemDamage() + (player.getItemInUseMaxCount()) ) / (double)stack.getMaxDamage(); + } + return super.getConjuredBowDurability(stack); + } + + // SECTION Particles + // =============================================================================================================== + + @Override + public void spawnParticle(WizardryParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge, float r, float g, float b, boolean doGravity, double radius){ + + // Colour values are now automatically clamped to between 0 and 1, as values outside this range seem to + // cause strange effects in 1.10 (or more specifically, particles that are bright pink!) + // TODO: This is a terrible dirty fix, but it'll do for now. Find a nicer way in future. + if(type != WizardryParticleType.MAGIC_FIRE) r = MathHelper.clamp_float(r, 0, 1); + g = MathHelper.clamp_float(g, 0, 1); + b = MathHelper.clamp_float(b, 0, 1); + + switch(type){ + + case BLIZZARD: + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleBlizzard(world, maxAge, x, z, radius, y)); + break; + case BRIGHT_DUST: + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleDust(world, x, y, z, velX, velY, velZ, r, g, b, false)); + break; + case DARK_MAGIC: + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleDarkMagic(world, x, y, z, velX, velY, velZ, r, g, b)); + break; + case DUST: + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleDust(world, x, y, z, velX, velY, velZ, r, g, b, true)); + break; + case ICE: + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleIce(world, x, y, z, velX, velY, velZ, maxAge)); + break; + case LEAF: + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleLeaf(world, x, y, z, velX, velY, velZ, maxAge)); + break; + case MAGIC_BUBBLE: + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleGiantBubble(world, x, y, z, velX, velY, velZ)); + break; + case MAGIC_FIRE: + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleMagicFlame(world, x, y, z, velX, velY, velZ, maxAge, r == 0 ? 1 + world.rand.nextFloat() : r)); + break; + case PATH: + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticlePath(world, x, y, z, velX, velY, velZ, r, g, b, maxAge)); + break; + case SNOW: + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleSnow(world, x, y, z, velX, velY, velZ)); + break; + case SPARK: + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleSpark(world, x, y, z, velX, velY, velZ)); + break; + case SPARKLE: + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleSparkle(world, x, y, z, velX, velY, velZ, r, g, b, maxAge, doGravity)); + break; + case SPARKLE_ROTATING: + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleRotatingSparkle(world, maxAge, x, z, radius, y, r, g, b)); + break; + default: + break; + } + } + + @Override + public void spawnTornadoParticle(World world, double x, double y, double z, double velX, double velZ, double radius, int maxAge, IBlockState block, BlockPos pos){ + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleTornado(world, maxAge, x, z, radius, y, velX, velZ, block).setBlockPos(pos));//, world.rand.nextInt(6))); + } + + // SECTION Packet Handlers + // =============================================================================================================== + + @Override + public void handleCastSpellPacket(PacketCastSpell.Message message){ + + World world = Minecraft.getMinecraft().theWorld; + Entity caster = world.getEntityByID(message.casterID); + Spell spell = Spell.get(message.spellID); + // Should always be true + if(caster instanceof EntityPlayer){ + + ((EntityPlayer)caster).setActiveHand(message.hand); + + // Duration isn't needed because it only ever affects things server-side, and anything that is + // seen client-side gets synced elsewhere. + spell.cast(world, (EntityPlayer)caster, message.hand, 0, new SpellModifiers()); + + // Updates the spell discovery data client-side. Could be done by calling sync(), but that means sending + // another packet unnecessarily. + if(WizardData.get((EntityPlayer)caster) != null){ + WizardData.get((EntityPlayer)caster).discoverSpell(spell); + } + + }else{ + Wizardry.logger.warn("Recieved a PacketCastSpell, but the caster ID was not the ID of a player"); + } + } + + @Override + public void handleCastContinuousSpellPacket(PacketCastContinuousSpell.Message message){ + + World world = Minecraft.getMinecraft().theWorld; + Entity caster = world.getEntityByID(message.casterID); + Spell spell = Spell.get(message.spellID); + // Should always be true + if(caster instanceof EntityPlayer){ + + WizardData data = WizardData.get((EntityPlayer)caster); + + if(data != null){ + if(data.isCasting()){ + WizardData.get((EntityPlayer)caster).stopCastingContinuousSpell(); + }else{ + WizardData.get((EntityPlayer)caster).startCastingContinuousSpell(spell, message.modifiers); + } + } + }else{ + Wizardry.logger.warn("Recieved a PacketCastContinuousSpell, but the caster ID was not the ID of a player"); + } + } + + @Override + public void handleNPCCastSpellPacket(PacketNPCCastSpell.Message message){ + + World world = Minecraft.getMinecraft().theWorld; + Entity caster = world.getEntityByID(message.casterID); + Entity target = message.targetID == -1 ? null : world.getEntityByID(message.targetID); + Spell spell = Spell.get(message.spellID); + // Should always be true + if(caster instanceof EntityLiving){ + + if(target instanceof EntityLivingBase){ + spell.cast(world, (EntityLiving)caster, message.hand, 0, (EntityLivingBase)target, message.modifiers); + } + + if(caster instanceof ISpellCaster){ + if(spell.isContinuous || spell instanceof None){ + ((ISpellCaster)caster).setContinuousSpell(spell); + ((EntityLiving)caster).setAttackTarget((EntityLivingBase)target); + } + } + }else{ + Wizardry.logger.warn("Recieved a PacketNPCCastSpell, but the caster ID was not the ID of an EntityLiving"); + } + } + + @Override + public void handleTransportationPacket(PacketTransportation.Message message){ + + World world = Minecraft.getMinecraft().theWorld; + Entity caster = world.getEntityByID(message.casterID); + // Moved from when the packet is sent to when it is received; fixes the sound not playing in first person. + caster.playSound(SoundEvents.BLOCK_PORTAL_TRAVEL, 1, 1); + + for(int i=0; i<20; i++){ + double radius = 1; + double angle = world.rand.nextDouble()*Math.PI*2; + double x = caster.posX + radius*Math.cos(angle); + double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble()*2; + double z = caster.posZ + radius*Math.sin(angle); + Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleSparkle(world, x, y, z, 0, 0.02, 0, 0.6f, 1.0f, 0.6f, 80 + world.rand.nextInt(10))); + } + for(int i=0; i<20; i++){ + double radius = 1; + double angle = world.rand.nextDouble()*Math.PI*2; + double x = caster.posX + radius*Math.cos(angle); + double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble()*2; + double z = caster.posZ + radius*Math.sin(angle); + world.spawnParticle(EnumParticleTypes.VILLAGER_HAPPY, x, y, z, 0, 0.02, 0); + } + for(int i=0; i<20; i++){ + double radius = 1; + double angle = world.rand.nextDouble()*Math.PI*2; + double x = caster.posX + radius*Math.cos(angle); + double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble()*2; + double z = caster.posZ + radius*Math.sin(angle); + world.spawnParticle(EnumParticleTypes.ENCHANTMENT_TABLE, x, y, z, 0, 0.02, 0); + } + } + + @Override + public void handlePlayerSyncPacket(Message message){ + + WizardData properties = WizardData.get(Minecraft.getMinecraft().thePlayer); + + if(properties != null){ + + properties.spellsDiscovered = message.spellsDiscovered; + + if(message.selectedMinionID == -1){ + properties.selectedMinion = null; + }else{ + Entity entity = Minecraft.getMinecraft().theWorld.getEntityByID(message.selectedMinionID); + + if(entity instanceof ISummonedCreature){ + properties.selectedMinion = new WeakReference((ISummonedCreature)entity); + }else{ + properties.selectedMinion = null; + } + } + } + } + + @Override + public void handleGlyphDataPacket(electroblob.wizardry.packet.PacketGlyphData.Message message){ + + SpellGlyphData data = SpellGlyphData.get(Minecraft.getMinecraft().theWorld); + + data.randomNames = new HashMap(); + data.randomDescriptions = new HashMap(); + + for(Spell spell : Spell.getSpells(Spell.allSpells)){ + // -1 because the none spell isn't included + data.randomNames.put(spell, message.names.get(spell.id() - 1)); + data.randomDescriptions.put(spell, message.descriptions.get(spell.id() - 1)); + } + } + + @Override + public void handleClairvoyancePacket(electroblob.wizardry.packet.PacketClairvoyance.Message message) { + Clairvoyance.spawnPathPaticles(Minecraft.getMinecraft().theWorld, message.path, message.durationMultiplier); + } + + // SECTION Rendering + // =============================================================================================================== + + private static final ResourceLocation ICE_WRAITH_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/ice_wraith.png"); + private static final ResourceLocation LIGHTNING_WRAITH_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_wraith.png"); + /** Static instance of the statue renderer, used to access the block breaking texture. */ + public static RenderStatue renderStatue; + + @Override + public void initialiseLayers(){ + LayerStone.initialiseLayers(); + } + + @Override + public void registerRenderers(){ + + // Minions + // Yet another advantage to the new system: turns out you don't even need to register the renderer if you + // just want the vanilla one for the mob you're extending. + + // An anonymous class in a lambda expression! No point writing a separate class really, is there? + RenderingRegistry.registerEntityRenderingHandler(EntityLightningWraith.class, manager -> new RenderBlaze(manager){ + @Override + protected ResourceLocation getEntityTexture(EntityBlaze entity){ + return LIGHTNING_WRAITH_TEXTURE; + } + }); + + RenderingRegistry.registerEntityRenderingHandler(EntityIceWraith.class, manager -> new RenderBlaze(manager){ + @Override + protected ResourceLocation getEntityTexture(EntityBlaze entity){ + return ICE_WRAITH_TEXTURE; + } + }); + + RenderingRegistry.registerEntityRenderingHandler(EntityIceGiant.class, RenderIceGiant::new); + RenderingRegistry.registerEntityRenderingHandler(EntityPhoenix.class, RenderPhoenix::new); + + // Projectiles + RenderingRegistry.registerEntityRenderingHandler(EntityMagicMissile.class, manager -> new RenderMagicArrow(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/magic_missile.png"), false, 8.0, 4.0, 16, 9, false)); + RenderingRegistry.registerEntityRenderingHandler(EntityIceShard.class, manager -> new RenderMagicArrow(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/ice_shard.png"), false, 8.0, 2.0, 16, 5, false)); + RenderingRegistry.registerEntityRenderingHandler(EntityLightningArrow.class, manager -> new RenderMagicArrow(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_arrow.png"), true, 8.0, 2.0, 16, 5, false)); + RenderingRegistry.registerEntityRenderingHandler(EntityDart.class, manager -> new RenderMagicArrow(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/dart.png"), false, 8.0, 2.0, 16, 5, true)); + RenderingRegistry.registerEntityRenderingHandler(EntityIceLance.class, manager -> new RenderMagicArrow(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/ice_lance.png"), false, 16.0, 3.0, 22, 5, true)); + + RenderingRegistry.registerEntityRenderingHandler(EntityForceArrow.class, RenderForceArrow::new); + + // Creatures + RenderingRegistry.registerEntityRenderingHandler(EntitySpiritWolf.class, manager -> new RenderSpiritWolf(manager, 0.5f)); + RenderingRegistry.registerEntityRenderingHandler(EntitySpiritHorse.class, manager -> new RenderSpiritHorse(manager, 0.5f)); + RenderingRegistry.registerEntityRenderingHandler(EntityWizard.class, RenderWizard::new); + RenderingRegistry.registerEntityRenderingHandler(EntityEvilWizard.class, RenderEvilWizard::new); + RenderingRegistry.registerEntityRenderingHandler(EntityDecoy.class, RenderDecoy::new); + + // Throwables + RenderingRegistry.registerEntityRenderingHandler(EntitySparkBomb.class, manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/entity/spark_bomb.png"), false)); + RenderingRegistry.registerEntityRenderingHandler(EntityFirebomb.class, manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/items/firebomb.png"), false)); + RenderingRegistry.registerEntityRenderingHandler(EntityPoisonBomb.class, manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/items/poison_bomb.png"), false)); + RenderingRegistry.registerEntityRenderingHandler(EntityIceCharge.class, manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/entity/ice_charge.png"), false)); + RenderingRegistry.registerEntityRenderingHandler(EntityForceOrb.class, manager -> new RenderProjectile(manager, 0.7f, new ResourceLocation(Wizardry.MODID, "textures/entity/force_orb.png"), true)); + RenderingRegistry.registerEntityRenderingHandler(EntitySpark.class, manager -> new RenderProjectile(manager, 0.4f, new ResourceLocation(Wizardry.MODID, "textures/entity/spark.png"), true)); + RenderingRegistry.registerEntityRenderingHandler(EntityDarknessOrb.class, manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/entity/darkness_orb.png"), true)); + RenderingRegistry.registerEntityRenderingHandler(EntityFirebolt.class, manager -> new RenderProjectile(manager, 0.2f, new ResourceLocation(Wizardry.MODID, "textures/entity/firebolt.png"), false)); + RenderingRegistry.registerEntityRenderingHandler(EntityLightningDisc.class, manager -> new RenderLightningDisc(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_sigil.png"), 2.0f)); + RenderingRegistry.registerEntityRenderingHandler(EntitySmokeBomb.class, manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/items/smoke_bomb.png"), false)); + + // Effects and constructs + RenderingRegistry.registerEntityRenderingHandler(EntityArc.class, RenderArc::new); + RenderingRegistry.registerEntityRenderingHandler(EntityBlackHole.class, RenderBlackHole::new); + RenderingRegistry.registerEntityRenderingHandler(EntityShield.class, RenderBlank::new); + RenderingRegistry.registerEntityRenderingHandler(EntityBubble.class, RenderBubble::new); + RenderingRegistry.registerEntityRenderingHandler(EntityHammer.class, RenderHammer::new); + RenderingRegistry.registerEntityRenderingHandler(EntityIceSpike.class, RenderIceSpike::new); + + // Stuff that doesn't render + RenderingRegistry.registerEntityRenderingHandler(EntityBlizzard.class, RenderBlank::new); + RenderingRegistry.registerEntityRenderingHandler(EntityTornado.class, RenderBlank::new); + RenderingRegistry.registerEntityRenderingHandler(EntityArrowRain.class, RenderBlank::new); + RenderingRegistry.registerEntityRenderingHandler(EntityShadowWraith.class, RenderBlank::new); + RenderingRegistry.registerEntityRenderingHandler(EntityForcefield.class, RenderBlank::new); + RenderingRegistry.registerEntityRenderingHandler(EntityThunderbolt.class, RenderBlank::new); + RenderingRegistry.registerEntityRenderingHandler(EntityStormElemental.class, RenderBlank::new); + RenderingRegistry.registerEntityRenderingHandler(EntityEarthquake.class, RenderBlank::new); + RenderingRegistry.registerEntityRenderingHandler(EntityHailstorm.class, RenderBlank::new); + + // Runes on ground + RenderingRegistry.registerEntityRenderingHandler(EntityHealAura.class, manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/healing_aura.png"), 5.0f, false)); + RenderingRegistry.registerEntityRenderingHandler(EntityFireSigil.class, manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/fire_sigil.png"), 2.0f, true)); + RenderingRegistry.registerEntityRenderingHandler(EntityFrostSigil.class, manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/frost_sigil.png"), 2.0f, true)); + RenderingRegistry.registerEntityRenderingHandler(EntityLightningSigil.class, manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_sigil.png"), 2.0f, true)); + RenderingRegistry.registerEntityRenderingHandler(EntityFireRing.class, manager -> new RenderFireRing(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/ring_of_fire.png"), 5.0f)); + RenderingRegistry.registerEntityRenderingHandler(EntityDecay.class, RenderDecay::new); + RenderingRegistry.registerEntityRenderingHandler(EntityLightningPulse.class, manager -> new RenderLightningPulse(manager, 8.0f)); + + // TESRs + ClientRegistry.bindTileEntitySpecialRenderer(TileEntityArcaneWorkbench.class, new RenderArcaneWorkbench()); + ClientRegistry.bindTileEntitySpecialRenderer(TileEntityStatue.class, renderStatue = new RenderStatue()); + ClientRegistry.bindTileEntitySpecialRenderer(TileEntityMagicLight.class, new RenderMagicLight()); + + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/EntityNameEntry.java b/src/main/java/electroblob/wizardry/client/EntityNameEntry.java new file mode 100644 index 00000000..3221e189 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/EntityNameEntry.java @@ -0,0 +1,76 @@ +package electroblob.wizardry.client; + +import java.util.HashMap; +import java.util.Map; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.resources.I18n; +import net.minecraft.entity.EntityList; +import net.minecraftforge.fml.client.config.GuiButtonExt; +import net.minecraftforge.fml.client.config.GuiEditArray; +import net.minecraftforge.fml.client.config.GuiEditArrayEntries; +import net.minecraftforge.fml.client.config.GuiEditArrayEntries.StringEntry; +import net.minecraftforge.fml.client.config.GuiSelectString; +import net.minecraftforge.fml.client.config.IConfigElement; + +/** [NYI] Intended as a way of choosing entities by name from all those currently registered, within the config file, so + * that users don't have to look up the entity IDs. I can't get this to work correctly at the moment. */ +public class EntityNameEntry extends StringEntry { + + protected final GuiButtonExt btnValue; + protected Object entityClass; + + public EntityNameEntry(GuiEditArray owningScreen, GuiEditArrayEntries owningEntryList, IConfigElement configElement, Object value) + { + super(owningScreen, owningEntryList, configElement, value); + this.btnValue = new GuiButtonExt(0, 0, 0, owningEntryList.controlWidth, 18, I18n.format(this.textFieldValue.getText())); + //this.btnValue.enabled = owningScreen.enabled; + } + + @Override + public void drawEntry(int slotIndex, int x, int y, int listWidth, int slotHeight, int mouseX, int mouseY, boolean isSelected) + { + //super.drawEntry(slotIndex, x, y, listWidth, slotHeight, tessellator, mouseX, mouseY, isSelected); + this.btnValue.xPosition = listWidth / 4; + this.btnValue.yPosition = y; + + String trans = I18n.format(this.textFieldValue.getText()); + if (!trans.equals(this.textFieldValue.getText())) + this.btnValue.displayString = trans; + else + this.btnValue.displayString = this.textFieldValue.getText(); + //btnValue.packedFGColour = value ? GuiUtils.getColorCode('2', true) : GuiUtils.getColorCode('4', true); + + this.btnValue.drawButton(owningEntryList.getMC(), mouseX, mouseY); + } + + @Override + public boolean mousePressed(int index, int x, int y, int mouseEvent, int relativeX, int relativeY) + { + if (this.btnValue.mousePressed(owningEntryList.getMC(), x, y)) + { + btnValue.playPressSound(owningEntryList.getMC().getSoundHandler()); + // Some sort of type incompatiblity meant that I had to do this first. + Map map = new HashMap(EntityList.CLASS_TO_NAME); + Minecraft.getMinecraft().displayGuiScreen(new GuiSelectString(this.owningScreen, configElement, index, map, this.getValue(), true)); + owningEntryList.recalculateState(); + return true; + } + + return super.mousePressed(index, x, y, mouseEvent, relativeX, relativeY); + } + + @Override + public void mouseReleased(int index, int x, int y, int mouseEvent, int relativeX, int relativeY) + { + this.btnValue.mouseReleased(x, y); + super.mouseReleased(index, x, y, mouseEvent, relativeX, relativeY); + } + + @Override + public Object getValue() + { + return this.textFieldValue.getText(); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/GuiArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/GuiArcaneWorkbench.java new file mode 100644 index 00000000..43f0470a --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/GuiArcaneWorkbench.java @@ -0,0 +1,230 @@ +package electroblob.wizardry.client; + +import org.lwjgl.input.Keyboard; + +import electroblob.wizardry.SpellGlyphData; +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.packet.PacketControlInput; +import electroblob.wizardry.packet.WizardryPacketHandler; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; +import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; +import electroblob.wizardry.util.WandHelper; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.resources.I18n; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; + +public class GuiArcaneWorkbench extends GuiContainer { + + private GuiButton applyBtn; + private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/arcane_workbench.png"); + + private IInventory playerInventory; + private IInventory arcaneWorkbenchInventory; + + private final int tooltipWidth = 164; + + public GuiArcaneWorkbench(InventoryPlayer invPlayer, TileEntityArcaneWorkbench entity) { + super(new ContainerArcaneWorkbench(invPlayer, entity)); + this.playerInventory = invPlayer; + this.arcaneWorkbenchInventory = entity; + xSize = 176; + ySize = 220; + } + + @Override + public void drawScreen(int p_73863_1_, int p_73863_2_, float p_73863_3_){ + + // Tests if there is a wand in the workbench and edits the positioning accordingly + if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getHasStack() && this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack().getItem() instanceof ItemWand){ + guiLeft = (this.width - this.xSize - tooltipWidth)/2; + this.applyBtn.xPosition = (this.width - tooltipWidth)/2 + 48; + }else{ + guiLeft = (this.width - this.xSize)/2; + this.applyBtn.xPosition = this.width/2 + 48; + } + + if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getHasStack()){ + this.applyBtn.enabled = true; + }else{ + this.applyBtn.enabled = false; + } + + super.drawScreen(p_73863_1_, p_73863_2_, p_73863_3_); + } + + @Override + public void drawGuiContainerBackgroundLayer(float f, int mouseX, int mouseY) { + + GlStateManager.pushAttrib(); + + GlStateManager.color(1F, 1F, 1F, 1F); + Minecraft.getMinecraft().renderEngine.bindTexture(texture); + + // Main inventory + drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize); + + // Changing slots + for(int i=0; i=0 && slot.yDisplayPosition >= 0) + this.drawTexturedModalRect(guiLeft + slot.xDisplayPosition - 10, guiTop + slot.yDisplayPosition - 10, + 0, 220, 36, 36); + } + + // Tooltip only drawn if there is a wand + if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getHasStack() && this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack().getItem() instanceof ItemWand){ + + // Tooltip box + drawTexturedModalRect(guiLeft + xSize, guiTop, xSize, 0, 256 - xSize - 4, ySize); + drawTexturedModalRect(guiLeft + 252, guiTop, xSize + 4, 0, tooltipWidth - 2*(256 - xSize - 4), ySize); + drawTexturedModalRect(guiLeft + xSize + tooltipWidth - (256 - xSize - 4), guiTop, xSize + 4, 0, 256 - xSize - 4, ySize); + + ItemStack wand = this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack(); + + Spell[] spells = WandHelper.getSpells(wand); + + int i=0; + + for(Spell spell : spells){ + + boolean discovered = true; + + if(!this.mc.thePlayer.capabilities.isCreativeMode && WizardData.get(this.mc.thePlayer) != null){ + discovered = WizardData.get(this.mc.thePlayer).hasSpellBeenDiscovered(spell); + } + // As of Wizardry 1.2, the icons have been split off into their own texture files to allow for add-on + // mods to add their own. + Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.element.getIcon() : Element.MAGIC.getIcon()); + + // Renders the little element icon + WizardryUtilities.drawTexturedRect(guiLeft + xSize + 5, guiTop + 34 + 10*i++, 8, 8); + } + + int x = 0; + int y = guiTop + 50 + spells.length*10; + + // Look how much shorter this is with the WandHelper class! + for(Item item : WandHelper.getSpecialUpgrades()){ + + int level = WandHelper.getUpgradeLevel(wand, item); + + if(level > 0){ + ItemStack stack = new ItemStack(item, level); + GlStateManager.enableDepth(); + this.itemRender.renderItemAndEffectIntoGUI(stack, guiLeft + xSize + 6 + x, y); + this.itemRender.renderItemOverlayIntoGUI(this.fontRendererObj, stack, guiLeft + xSize + 6 + x, y, null); + x += 18; + GlStateManager.disableDepth(); + } + } + } + + Minecraft.getMinecraft().renderEngine.bindTexture(texture); + + // Fixes the bug that caused the slot hightlight to render opaque. I don't know why it works, it just works! + GlStateManager.disableBlend(); + GlStateManager.enableAlpha(); + + GlStateManager.popAttrib(); + } + + @Override + protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY){ + + this.fontRendererObj.drawString(this.arcaneWorkbenchInventory.hasCustomName() ? this.arcaneWorkbenchInventory.getName() : I18n.format(this.arcaneWorkbenchInventory.getName()), 8, 6, 4210752); + this.fontRendererObj.drawString(this.playerInventory.hasCustomName() ? this.playerInventory.getName() : I18n.format(this.playerInventory.getName()), 8, this.ySize - 96 + 2, 4210752); + + if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getHasStack() && this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack().getItem() instanceof ItemWand){ + + ItemStack wand = this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack(); + + this.fontRendererObj.drawStringWithShadow("\u00A7f" + wand.getDisplayName(), xSize + 6, 6, 0); + this.fontRendererObj.drawStringWithShadow("\u00A77" + I18n.format("container.wizardry:arcane_workbench.mana") + " " + (wand.getMaxDamage() - wand.getItemDamage()) + "/" + wand.getMaxDamage(), xSize + 6, 20, 0); + + Spell[] spells = WandHelper.getSpells(wand); + + int y = 34; + + for(Spell spell : spells){ + + boolean discovered = true; + + if(!this.mc.thePlayer.capabilities.isCreativeMode && WizardData.get(this.mc.thePlayer) != null){ + discovered = WizardData.get(this.mc.thePlayer).hasSpellBeenDiscovered(spell); + } + + if(discovered){ + this.fontRendererObj.drawStringWithShadow(spell.getDisplayNameWithFormatting(), xSize + 16, y, 0); + }else{ + this.mc.standardGalacticFontRenderer.drawStringWithShadow("\u00A79" + SpellGlyphData.getGlyphName(spell, this.mc.theWorld), xSize + 16, y, 0); + } + y += 10; + } + + if(WandHelper.getTotalUpgrades(wand) > 0){ + + this.fontRendererObj.drawStringWithShadow("\u00A7f" + I18n.format("container.wizardry:arcane_workbench.upgrades"), xSize + 6, y + 6, 0); + + int x = 0; + y = 50 + spells.length*10; + // Wand upgrade tooltips + for(Item item : WandHelper.getSpecialUpgrades()){ + + int level = WandHelper.getUpgradeLevel(wand, item); + + if(level > 0){ + // The javadoc for isPointInRegion is ambiguous; what it means is that the REGION is + // relative to the GUI but the POINT isn't. + if(isPointInRegion(xSize + 6 + x, y, 16, 16, mouseX, mouseY)){ + ItemStack stack = new ItemStack(item, level); + this.renderToolTip(stack, mouseX - guiLeft, mouseY - guiTop); + } + x += 18; + } + } + } + } + } + + @Override + public void initGui(){ + this.mc.thePlayer.openContainer = this.inventorySlots; + this.guiLeft = (this.width - this.xSize) / 2; + this.guiTop = (this.height - this.ySize) / 2; + Keyboard.enableRepeatEvents(true); + this.buttonList.clear(); + this.buttonList.add(this.applyBtn = new GuiButtonApply(0, this.width/2 + 48, this.height/2 + 3)); + } + + @Override + public void onGuiClosed(){ + super.onGuiClosed(); + Keyboard.enableRepeatEvents(false); + } + + @Override + protected void actionPerformed(GuiButton button){ + if(button.enabled){ + if(button.id == 0){ + // Packet building + IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.APPLY_BUTTON); + WizardryPacketHandler.net.sendToServer(msg); + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/GuiButtonApply.java b/src/main/java/electroblob/wizardry/client/GuiButtonApply.java new file mode 100644 index 00000000..fd54d344 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/GuiButtonApply.java @@ -0,0 +1,41 @@ +package electroblob.wizardry.client; + +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.resources.I18n; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +class GuiButtonApply extends GuiButton { + + public GuiButtonApply(int id, int x, int y){ + + super(id, x, y, 32, 16, I18n.format("container.wizardry:arcane_workbench.apply")); + } + + @Override + public void drawButton(Minecraft minecraft, int mouseX, int mouseY){ + + // Whether the button is highlighted + this.hovered = mouseX >= this.xPosition && mouseY >= this.yPosition && mouseX < this.xPosition + this.width && mouseY < this.yPosition + this.height; + + int k = 36; + int l = 220; + int colour = 14737632; + + if(this.enabled){ + if(this.hovered){ + k += this.width*2; + colour = 16777120; + } + }else{ + k += this.width; + colour = 10526880; + } + + WizardryUtilities.drawTexturedRect(this.xPosition, this.yPosition, k, l, this.width, this.height, 256, 256); + this.drawCenteredString(minecraft.fontRendererObj, this.displayString, this.xPosition + this.width / 2, this.yPosition + (this.height - 8) / 2, colour); + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/GuiButtonInvisible.java b/src/main/java/electroblob/wizardry/client/GuiButtonInvisible.java new file mode 100644 index 00000000..3cc2f12e --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/GuiButtonInvisible.java @@ -0,0 +1,23 @@ +package electroblob.wizardry.client; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +class GuiButtonInvisible extends GuiButton { + + public GuiButtonInvisible(int id, int x, int y, int width, int height){ + + super(id, x, y, width, height, ""); + } + + /** + * Draws this button to the screen. + */ + public void drawButton(Minecraft par1Minecraft, int par2, int par3){ + this.hovered = par2 >= this.xPosition && par3 >= this.yPosition && par2 < this.xPosition + this.width && par3 < this.yPosition + this.height; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/GuiButtonTurnPage.java b/src/main/java/electroblob/wizardry/client/GuiButtonTurnPage.java new file mode 100644 index 00000000..b4b7e68b --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/GuiButtonTurnPage.java @@ -0,0 +1,52 @@ +package electroblob.wizardry.client; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +class GuiButtonTurnPage extends GuiButton { + + /** True for pointing right (next page), false for pointing left (previous page). */ + private final boolean nextPage; + + private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png"); + + public GuiButtonTurnPage(int id, int x, int y, boolean isNextPage) + { + super(id, x, y, 23, 13, ""); + this.nextPage = isNextPage; + } + + /** + * Draws this button to the screen. + */ + public void drawButton(Minecraft par1Minecraft, int par2, int par3) + { + if (this.visible) + { + boolean flag = par2 >= this.xPosition && par3 >= this.yPosition && par2 < this.xPosition + this.width && par3 < this.yPosition + this.height; + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + par1Minecraft.getTextureManager().bindTexture(texture); + int k = 0; + int l = 192; + + if (flag) + { + k += 23; + } + + if (!this.nextPage) + { + l += 13; + } + + WizardryUtilities.drawTexturedRect(this.xPosition, this.yPosition, k, l, 23, 13, 288, 256); + } + } +} diff --git a/src/main/java/electroblob/wizardry/client/GuiConfigWizardry.java b/src/main/java/electroblob/wizardry/client/GuiConfigWizardry.java new file mode 100644 index 00000000..587a8603 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/GuiConfigWizardry.java @@ -0,0 +1,80 @@ +package electroblob.wizardry.client; + +import java.util.ArrayList; +import java.util.List; + +import electroblob.wizardry.Settings; +import electroblob.wizardry.Wizardry; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraftforge.common.config.ConfigElement; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.fml.client.config.DummyConfigElement.DummyCategoryElement; +import net.minecraftforge.fml.client.config.GuiConfig; +import net.minecraftforge.fml.client.config.GuiConfigEntries; +import net.minecraftforge.fml.client.config.GuiConfigEntries.CategoryEntry; +import net.minecraftforge.fml.client.config.IConfigElement; + +public class GuiConfigWizardry extends GuiConfig { + + public GuiConfigWizardry(GuiScreen parent){ + super(parent, getConfigEntries(), Wizardry.MODID, false, false, Wizardry.NAME + " - " + I18n.format("config.wizardry.title.general")); + //this.titleLine2 = "File location: " + Wizardry.config.getConfigFile().getAbsolutePath(); + } + + private static List getConfigEntries(){ + List configList = new ArrayList(1); + configList.add(new DummyCategoryElement("spellsConfig", "config.wizardry.category." + Settings.SPELLS_CATEGORY, SpellsCategory.class)); + configList.add(new DummyCategoryElement("resistancesConfig", "config.wizardry.category." + Settings.RESISTANCES_CATEGORY, ResistancesCategory.class)); + configList.addAll(new ConfigElement(Wizardry.settings.getConfigCategory(Configuration.CATEGORY_GENERAL)).getChildElements()); + return configList; + } + + /** Spells category of the config gui. This adds a button which opens up the spells category config. */ + public static class SpellsCategory extends CategoryEntry + { + public SpellsCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) + { + super(owningScreen, owningEntryList, prop); + } + + @Override + protected GuiScreen buildChildScreen() + { + // This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. + // The parent GuiConfig object's entryList will also be refreshed to reflect the changes. + GuiConfig spellsMenu = new GuiConfig(this.owningScreen, + (new ConfigElement(Wizardry.settings.getConfigCategory(Settings.SPELLS_CATEGORY))).getChildElements(), + this.owningScreen.modID, Settings.SPELLS_CATEGORY, false, false, + Wizardry.NAME + " - " + I18n.format("config.wizardry.title." + Settings.SPELLS_CATEGORY)); + + spellsMenu.titleLine2 = I18n.format("config.wizardry.subtitle." + Settings.SPELLS_CATEGORY); + + return spellsMenu; + } + } + + /** Resistances category of the config gui. */ + public static class ResistancesCategory extends CategoryEntry + { + public ResistancesCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) + { + super(owningScreen, owningEntryList, prop); + } + + @Override + protected GuiScreen buildChildScreen() + { + // This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. + // The parent GuiConfig object's entryList will also be refreshed to reflect the changes. + GuiConfig idsMenu = new GuiConfig(this.owningScreen, + (new ConfigElement(Wizardry.settings.getConfigCategory(Settings.RESISTANCES_CATEGORY))).getChildElements(), + this.owningScreen.modID, Settings.RESISTANCES_CATEGORY, false, false, + Wizardry.NAME + " - " + I18n.format("config.wizardry.title." + Settings.RESISTANCES_CATEGORY)); + + idsMenu.titleLine2 = I18n.format("config.wizardry.subtitle." + Settings.RESISTANCES_CATEGORY); + + return idsMenu; + } + } +} diff --git a/src/main/java/electroblob/wizardry/client/GuiPortableCrafting.java b/src/main/java/electroblob/wizardry/client/GuiPortableCrafting.java new file mode 100644 index 00000000..d4f8e225 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/GuiPortableCrafting.java @@ -0,0 +1,39 @@ +package electroblob.wizardry.client; + +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.resources.I18n; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.ContainerWorkbench; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +/** Crafting table GUI that doesn't require a crafting table container object. */ +public class GuiPortableCrafting extends GuiContainer { + + private static final ResourceLocation craftingTableGuiTextures = new ResourceLocation("textures/gui/container/crafting_table.png"); + + public GuiPortableCrafting(InventoryPlayer p_i1084_1_, World p_i1084_2_, BlockPos pos) + { + super(new ContainerWorkbench(p_i1084_1_, p_i1084_2_, pos)); + } + + /** + * Draw the foreground layer for the GuiContainer (everything in front of the items) + */ + protected void drawGuiContainerForegroundLayer(int p_146979_1_, int p_146979_2_) + { + this.fontRendererObj.drawString(I18n.format("container.crafting"), 28, 6, 4210752); + this.fontRendererObj.drawString(I18n.format("container.inventory"), 8, this.ySize - 96 + 2, 4210752); + } + + protected void drawGuiContainerBackgroundLayer(float p_146976_1_, int p_146976_2_, int p_146976_3_) + { + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + this.mc.getTextureManager().bindTexture(craftingTableGuiTextures); + int k = (this.width - this.xSize) / 2; + int l = (this.height - this.ySize) / 2; + this.drawTexturedModalRect(k, l, 0, 0, this.xSize, this.ySize); + } +} diff --git a/src/main/java/electroblob/wizardry/client/GuiSpellBook.java b/src/main/java/electroblob/wizardry/client/GuiSpellBook.java new file mode 100644 index 00000000..c7a90cb2 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/GuiSpellBook.java @@ -0,0 +1,127 @@ +package electroblob.wizardry.client; + +import org.lwjgl.input.Keyboard; + +import electroblob.wizardry.SpellGlyphData; +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.ResourceLocation; + +public class GuiSpellBook extends GuiScreen { + + private int xSize, ySize; + private Spell spell; + + private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/spellbook.png"); + + public GuiSpellBook(Spell spell) { + super(); + xSize = 288; + ySize = 180; + this.spell = spell; + } + + /** + * Draws the screen and all the components in it. + */ + public void drawScreen(int par1, int par2, float par3) { + + int xPos = this.width/2 - xSize/2; + int yPos = this.height/2 - this.ySize/2; + + EntityPlayer player = Minecraft.getMinecraft().thePlayer; + + boolean discovered = true; + if(Wizardry.settings.discoveryMode && !player.capabilities.isCreativeMode && WizardData.get(player) != null + && !WizardData.get(player).hasSpellBeenDiscovered(spell)){ + discovered = false; + } + + // Draws spell illustration on opposite page, underneath the book so it shows through the hole. + Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon()); + WizardryUtilities.drawTexturedRect(xPos + 145, yPos + 20, 0, 0, 128, 128, 128, 128); + + Minecraft.getMinecraft().renderEngine.bindTexture(texture); + WizardryUtilities.drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256); + + super.drawScreen(par1, par2, par3); + + if(discovered){ + this.fontRendererObj.drawString(spell.getDisplayName(), xPos+17, yPos+14, 0); + this.fontRendererObj.drawString(spell.type.getDisplayName(), xPos+17, yPos+25, 0x777777); + }else{ + this.mc.standardGalacticFontRenderer.drawString(SpellGlyphData.getGlyphName(spell, player.worldObj), xPos+17, yPos+14, 0); + this.mc.standardGalacticFontRenderer.drawString(spell.type.getDisplayName(), xPos+17, yPos+25, 0x777777); + } + + this.fontRendererObj.drawString("-------------------", xPos+17, yPos+34, 0); + + if(spell.tier == Tier.BASIC){ + // Basic is usually white but this doesn't show up. + this.fontRendererObj.drawString("Tier: \u00A77" + Tier.BASIC.getDisplayName(), xPos+17, yPos+44, 0); + }else{ + this.fontRendererObj.drawString("Tier: " + spell.tier.getDisplayNameWithFormatting(), xPos+17, yPos+44, 0); + } + + String element = "Element: " + spell.element.getFormattingCode() + spell.element.getDisplayName(); + if(!discovered) element = "Element: ?"; + this.fontRendererObj.drawString(element, xPos+17, yPos+56, 0); + + String manaCost = "Mana Cost: " + spell.cost; + if(spell.isContinuous) manaCost = "Mana Cost: " + spell.cost + "/second"; + if(!discovered) manaCost = "Mana Cost: ?"; + this.fontRendererObj.drawString(manaCost, xPos+17, yPos+68, 0); + + if(discovered){ + this.fontRendererObj.drawSplitString(spell.getDescription(), xPos+17, yPos+82, 118, 0); + }else{ + this.mc.standardGalacticFontRenderer.drawSplitString(SpellGlyphData.getGlyphDescription(spell, player.worldObj), xPos+17, yPos+82, 118, 0); + } + + /* + // Word wrapping + int charNumber = 0; + int lineNumber = 0; + + while(charNumber < spell.desc.length()){ + int lineLength = 0; + String line; + if(spell.desc.length() - charNumber > 22){ + for(int i = charNumber; i < charNumber+23; i++){ + if(spell.desc.charAt(i) == ' '){ + lineLength = i - charNumber; + } + } + line = spell.desc.substring(charNumber, charNumber + lineLength); + }else{ + line = spell.desc.substring(charNumber, spell.desc.length()); + charNumber = spell.desc.length(); + } + this.fontRendererObj.drawString("\u00A7o" + line, xPos+17, yPos+82+10*lineNumber, 0); + charNumber+=(lineLength+1); + lineNumber++; + } + */ + } + + public void initGui() + { + super.initGui(); + Keyboard.enableRepeatEvents(true); + this.buttonList.clear(); + } + + public void onGuiClosed() + { + super.onGuiClosed(); + Keyboard.enableRepeatEvents(false); + } +} + diff --git a/src/main/java/electroblob/wizardry/client/GuiSpellDisplay.java b/src/main/java/electroblob/wizardry/client/GuiSpellDisplay.java new file mode 100644 index 00000000..dc27a3eb --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/GuiSpellDisplay.java @@ -0,0 +1,155 @@ +package electroblob.wizardry.client; + +import java.util.List; + +import electroblob.wizardry.Settings.GuiPosition; +import electroblob.wizardry.SpellGlyphData; +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.WandHelper; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.GlStateManager.DestFactor; +import net.minecraft.client.renderer.GlStateManager.SourceFactor; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.event.RenderGameOverlayEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +public class GuiSpellDisplay extends Gui { + + private Minecraft mc; + + private static final ResourceLocation hudTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/spell_hud.png"); + + public GuiSpellDisplay(Minecraft par1Minecraft) { + super(); + this.mc = par1Minecraft; + } + + @SubscribeEvent + public void draw(RenderGameOverlayEvent event){ + + EntityPlayer player = this.mc.thePlayer; + + // If the player has a wand in each hand, only displays for the one in the main hand. + + ItemStack wand = player.getHeldItemMainhand(); + + if(wand == null || !(wand.getItem() instanceof ItemWand)){ + wand = player.getHeldItemOffhand(); + // If the player isn't holding a wand, then nothing else needs to be done. + if(wand == null || !(wand.getItem() instanceof ItemWand)) return; + } + + int width = event.getResolution().getScaledWidth(); + int height = event.getResolution().getScaledHeight(); + + Spell spell = WandHelper.getCurrentSpell(wand); + int cooldown = WandHelper.getCurrentCooldown(wand); + + float cooldownMultiplier = 1.0f - WandHelper.getUpgradeLevel(wand, WizardryItems.cooldown_upgrade)*Constants.COOLDOWN_REDUCTION_PER_LEVEL; + + if(player.isPotionActive(WizardryPotions.font_of_mana)){ + // Dividing by this rather than setting it takes upgrades and font of mana into account simultaneously + cooldownMultiplier /= 2 + player.getActivePotionEffect(WizardryPotions.font_of_mana).getAmplifier(); + } + + // Coordinates of the top left corner of the HUD. + int left = 0; + int top = 0; + boolean mirror = false; + + if(Wizardry.settings.spellHUDPosition == GuiPosition.BOTTOM_LEFT){ + left = 0; + top = height-36; + }else if(Wizardry.settings.spellHUDPosition == GuiPosition.TOP_LEFT){ + left = 0; + top = 0; + }else if(Wizardry.settings.spellHUDPosition == GuiPosition.TOP_RIGHT){ + left = width-128; + top = 0; + mirror = true; + }else if(Wizardry.settings.spellHUDPosition == GuiPosition.BOTTOM_RIGHT){ + left = width-128; + top = height-36; + mirror = true; + } + + boolean discovered = true; + + if(!player.capabilities.isCreativeMode && WizardData.get(player) != null){ + discovered = WizardData.get(player).hasSpellBeenDiscovered(spell); + } + + if(event.getType() == RenderGameOverlayEvent.ElementType.TEXT){ + + // Makes spells greyed out if they are in cooldown or if the player has the arcane jammer effect + String colour = cooldown > 0 || player.isPotionActive(WizardryPotions.arcane_jammer) ? "\u00A78" : spell.element.getFormattingCode(); + if(!discovered) colour = "\u00A79"; + String spellName = discovered ? spell.getDisplayName() : SpellGlyphData.getGlyphName(spell, player.worldObj); + FontRenderer font = discovered ? this.mc.fontRendererObj : this.mc.standardGalacticFontRenderer; + + int maxWidth = 90; + + if(font.getStringWidth(spellName) <= maxWidth){ + // Single line is rendered more centrally + font.drawStringWithShadow(colour + spellName, mirror ? left+5 : left+41, top+13, 0xffffffff); + + }else{ + + int lineNumber = 0; + + List lines = font.listFormattedStringToWidth(spellName, maxWidth); + + for(Object line : lines){ + if(line instanceof String){ + font.drawStringWithShadow(colour + (String)line, mirror ? left+5 : left+41, top+6 + 11*lineNumber, 0xffffffff); + } + lineNumber++; + } + } + + }else if(event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR){ + + GlStateManager.pushAttrib(); + + GlStateManager.enableBlend(); + GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA); + GlStateManager.color(1, 1, 1); + + this.mc.renderEngine.bindTexture(hudTexture); + + // Background of spell hud + this.drawTexturedModalRect(left, top, 0, mirror ? 36 : 0, 128, 36); + + // Cooldown bar + if(cooldown > 0){ + this.drawTexturedModalRect(mirror ? left+5 : left+41, height-8, 128, 6, 82, 6); + + int l = (int)(((double)(spell.cooldown * cooldownMultiplier - cooldown) + / (double)(spell.cooldown * cooldownMultiplier)) * 82); + + this.drawTexturedModalRect(mirror ? left+5 : left+41, height-8, 128, 0, l, 6); + } + + // Spell illustration + this.mc.renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon()); + + WizardryUtilities.drawTexturedRect(mirror ? left+94 : left+2, top+2, 0, 0, 32, 32, 32, 32); + + GlStateManager.popAttrib(); + } + } + +} diff --git a/src/main/java/electroblob/wizardry/client/GuiWizardHandbook.java b/src/main/java/electroblob/wizardry/client/GuiWizardHandbook.java new file mode 100644 index 00000000..5cb8ee9e --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/GuiWizardHandbook.java @@ -0,0 +1,809 @@ +package electroblob.wizardry.client; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.Charsets; +import org.lwjgl.input.Keyboard; +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryBlocks; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; + +public class GuiWizardHandbook extends GuiScreen { + + private int xSize, ySize; + private int pageNumber = 0; + + private static final int PAGE_WIDTH = 120; + /** The integer colour for black passed into the font renderer methods. This used to be 0 but that's now white + * for some reason, so I've made a it a constant in case it changes again. */ + // I think this is actually ever-so-slightly lighter than pure black, but the difference is unnoticeable. + private static final int BLACK = 1; + + public static final ResourceLocation regularHandbook = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png"); + public static final ResourceLocation ore = new ResourceLocation(Wizardry.MODID, "textures/gui/ore_picture.png"); + public static final ResourceLocation crystal = new ResourceLocation(Wizardry.MODID, "textures/items/magic_crystal.png"); + public static final ResourceLocation workbenchGui = new ResourceLocation(Wizardry.MODID, "textures/gui/arcane_workbench.png"); + public static final ResourceLocation craftingGrids = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook_recipes.png"); + + private ArrayList> text; + private ArrayList

    sections; + + private int guiPage, imagePage; + + public GuiWizardHandbook() { + super(); + xSize = 288; + ySize = 180; + } + + @Override + public void drawScreen(int mouseX, int mouseY, float par3){ + + int xPos = this.width/2 - xSize/2; + int yPos = this.height/2 - this.ySize/2; + + // Tests for crafting recipes section + if(pageNumber >= (sections.get(sections.size()-1).pageNumber-1)/2 && pageNumber < (sections.get(sections.size()-1).pageNumber-1)/2 + 4){ + Minecraft.getMinecraft().renderEngine.bindTexture(craftingGrids); + }else{ + Minecraft.getMinecraft().renderEngine.bindTexture(regularHandbook); + } + + WizardryUtilities.drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256); + + // Arcane workbench gui picture + if(pageNumber == (this.guiPage-1)/2){ + Minecraft.getMinecraft().renderEngine.bindTexture(workbenchGui); + this.drawTexturedModalRect(this.guiPage % 2 == 1 ? xPos + 17 : this.width/2 + 7, yPos + 14, 28, 12, 120, 118); + } + + // Magic crystal and crystal ore images + if(pageNumber == (this.imagePage-1)/2){ + + Minecraft.getMinecraft().renderEngine.bindTexture(ore); + WizardryUtilities.drawTexturedRect(this.imagePage % 2 == 1 ? xPos + 17 : this.width/2 + 7, yPos + 80, 0, 0, 64, 64, 64, 64); + + Minecraft.getMinecraft().renderEngine.bindTexture(crystal); + drawTexturedStretchedRect(this.imagePage % 2 == 1 ? xPos + 17 + 64 : this.width/2 + 7 + 62, yPos + 80, 0, 0, 64, 64, 1, 1); + + } + + this.fontRendererObj.drawString("" + (pageNumber*2 + 1), xPos + xSize/4 - 3, yPos + ySize - 20, 0); + this.fontRendererObj.drawString("" + (pageNumber*2 + 2), xPos + 3*xSize/4 - 5, yPos + ySize - 20, 0); + + super.drawScreen(mouseX, mouseY, par3); + + int lineNumber = 0; + + if(pageNumber == 1){ + for(Section s : sections){ + s.drawContents(); + } + }else{ + for(Section s : sections){ + s.hideButton(); + } + } + + for(String paragraph : text.get(pageNumber*2)){ + + this.fontRendererObj.drawSplitString(paragraph, xPos + 17, yPos + 14 + lineNumber*this.fontRendererObj.FONT_HEIGHT, PAGE_WIDTH, BLACK); + + List list = new ArrayList(this.fontRendererObj.listFormattedStringToWidth(paragraph, GuiWizardHandbook.PAGE_WIDTH)); + + lineNumber += list.size(); + } + + lineNumber = 0; + + // Prevents crash when the last page is blank (and hence is not in the list of pages) + if(text.size() > pageNumber*2 + 1){ + for(String paragraph : text.get(pageNumber*2 + 1)){ + + // First page is centred + if(pageNumber == 0){ + int startX = this.width/2 + 7 + PAGE_WIDTH/2 - this.fontRendererObj.getStringWidth(paragraph)/2; + this.fontRendererObj.drawSplitString(paragraph, startX, yPos + 14 + lineNumber*this.fontRendererObj.FONT_HEIGHT, PAGE_WIDTH, BLACK); + }else{ + this.fontRendererObj.drawSplitString(paragraph, this.width/2 + 7, yPos + 14 + lineNumber*this.fontRendererObj.FONT_HEIGHT, PAGE_WIDTH, BLACK); + } + + List list = new ArrayList(this.fontRendererObj.listFormattedStringToWidth(paragraph, GuiWizardHandbook.PAGE_WIDTH)); + + lineNumber += list.size(); + } + } + + ItemStack[][] craftingGrid; + ItemStack craftingResult; + + // Tooltips are rendered after recipes to prevent tooltips on the left appearing behind items on the right. + if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2){ + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(Items.GOLD_NUGGET); + craftingGrid[1][0] = new ItemStack(Blocks.CARPET, 1, 10); + craftingGrid[2][0] = new ItemStack(Items.GOLD_NUGGET); + craftingGrid[0][1] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][1] = new ItemStack(Blocks.LAPIS_BLOCK); + craftingGrid[2][1] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[0][2] = new ItemStack(Blocks.STONE); + craftingGrid[1][2] = new ItemStack(Blocks.STONE); + craftingGrid[2][2] = new ItemStack(Blocks.STONE); + craftingResult = new ItemStack(WizardryBlocks.arcane_workbench); + this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[2][0] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][1] = new ItemStack(Items.STICK); + craftingGrid[0][2] = new ItemStack(Items.GOLD_NUGGET); + craftingResult = new ItemStack(WizardryItems.magic_wand); + this.renderCraftingRecipe(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[1][0] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[0][1] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][1] = new ItemStack(Items.BOOK); + craftingGrid[1][2] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[2][1] = new ItemStack(WizardryItems.magic_crystal); + craftingResult = new ItemStack(WizardryItems.spell_book, 1, 1); + this.renderCraftingRecipe(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(Items.BOOK); + craftingGrid[1][0] = new ItemStack(WizardryItems.magic_crystal); + craftingResult = new ItemStack(WizardryItems.wizard_handbook); + this.renderCraftingRecipe(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + + }else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 1){ + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(WizardryBlocks.crystal_flower); + craftingResult = new ItemStack(WizardryItems.magic_crystal, 2); + this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][0] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[2][0] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[0][1] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][1] = new ItemStack(Items.GLASS_BOTTLE); + craftingGrid[2][1] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[0][2] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][2] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[2][2] = new ItemStack(WizardryItems.magic_crystal); + craftingResult = new ItemStack(WizardryItems.mana_flask); + this.renderCraftingRecipe(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[1][0] = new ItemStack(Blocks.STONE); + craftingGrid[0][1] = new ItemStack(Blocks.STONE); + craftingGrid[1][1] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][2] = new ItemStack(Blocks.STONE); + craftingGrid[2][1] = new ItemStack(Blocks.STONE); + craftingResult = new ItemStack(WizardryBlocks.transportation_stone, 2); + this.renderCraftingRecipe(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[1][0] = new ItemStack(Items.STRING); + craftingGrid[0][1] = new ItemStack(Items.STRING); + craftingGrid[1][1] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][2] = new ItemStack(Items.STRING); + craftingGrid[2][1] = new ItemStack(Items.STRING); + craftingResult = new ItemStack(WizardryItems.magic_silk, 2); + this.renderCraftingRecipe(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + + }else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 2){ + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[1][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); + craftingResult = new ItemStack(WizardryItems.wizard_hat); + this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[1][1] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[0][2] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[1][2] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][2] = new ItemStack(WizardryItems.magic_silk); + craftingResult = new ItemStack(WizardryItems.wizard_robe); + this.renderCraftingRecipe(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[1][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[0][2] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][2] = new ItemStack(WizardryItems.magic_silk); + craftingResult = new ItemStack(WizardryItems.wizard_leggings); + this.renderCraftingRecipe(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); + craftingResult = new ItemStack(WizardryItems.wizard_boots); + this.renderCraftingRecipe(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + + }else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 3){ + + if(Wizardry.settings.useAlternateScrollRecipe){ + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(Items.PAPER); + craftingGrid[1][0] = new ItemStack(Items.STRING); + craftingGrid[2][0] = new ItemStack(WizardryItems.magic_crystal); + craftingResult = new ItemStack(WizardryItems.blank_scroll); + this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + }else{ + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(Items.PAPER); + craftingGrid[1][0] = new ItemStack(Items.STRING); + craftingResult = new ItemStack(WizardryItems.blank_scroll); + this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + } + + if(Wizardry.settings.firebombIsCraftable){ + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(Items.BLAZE_POWDER); + craftingGrid[1][0] = new ItemStack(Items.BLAZE_POWDER); + craftingGrid[0][1] = new ItemStack(Items.GLASS_BOTTLE); + craftingGrid[1][1] = new ItemStack(Items.GUNPOWDER); + craftingResult = new ItemStack(WizardryItems.firebomb, 3); + this.renderCraftingRecipe(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + } + + if(Wizardry.settings.poisonBombIsCraftable){ + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(Items.SPIDER_EYE); + craftingGrid[1][0] = new ItemStack(Items.SPIDER_EYE); + craftingGrid[0][1] = new ItemStack(Items.GLASS_BOTTLE); + craftingGrid[1][1] = new ItemStack(Items.GUNPOWDER); + craftingResult = new ItemStack(WizardryItems.poison_bomb, 3); + this.renderCraftingRecipe(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + } + + if(Wizardry.settings.smokeBombIsCraftable){ + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(Items.COAL); + craftingGrid[1][0] = new ItemStack(Items.COAL); + craftingGrid[0][1] = new ItemStack(Items.GLASS_BOTTLE); + craftingGrid[1][1] = new ItemStack(Items.GUNPOWDER); + craftingResult = new ItemStack(WizardryItems.smoke_bomb, 3); + this.renderCraftingRecipe(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + } + + } + + if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2){ + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(Items.GOLD_NUGGET); + craftingGrid[1][0] = new ItemStack(Blocks.CARPET, 1, 10); + craftingGrid[2][0] = new ItemStack(Items.GOLD_NUGGET); + craftingGrid[0][1] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][1] = new ItemStack(Blocks.LAPIS_BLOCK); + craftingGrid[2][1] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[0][2] = new ItemStack(Blocks.STONE); + craftingGrid[1][2] = new ItemStack(Blocks.STONE); + craftingGrid[2][2] = new ItemStack(Blocks.STONE); + craftingResult = new ItemStack(WizardryBlocks.arcane_workbench); + this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[2][0] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][1] = new ItemStack(Items.STICK); + craftingGrid[0][2] = new ItemStack(Items.GOLD_NUGGET); + craftingResult = new ItemStack(WizardryItems.magic_wand); + this.renderCraftingTooltips(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[1][0] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[0][1] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][1] = new ItemStack(Items.BOOK); + craftingGrid[1][2] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[2][1] = new ItemStack(WizardryItems.magic_crystal); + craftingResult = new ItemStack(WizardryItems.spell_book, 1, 1); + this.renderCraftingTooltips(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(Items.BOOK); + craftingGrid[1][0] = new ItemStack(WizardryItems.magic_crystal); + craftingResult = new ItemStack(WizardryItems.wizard_handbook); + this.renderCraftingTooltips(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + + }else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 1){ + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(WizardryBlocks.crystal_flower); + craftingResult = new ItemStack(WizardryItems.magic_crystal, 2); + this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][0] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[2][0] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[0][1] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][1] = new ItemStack(Items.GLASS_BOTTLE); + craftingGrid[2][1] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[0][2] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][2] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[2][2] = new ItemStack(WizardryItems.magic_crystal); + craftingResult = new ItemStack(WizardryItems.mana_flask); + this.renderCraftingTooltips(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[1][0] = new ItemStack(Blocks.STONE); + craftingGrid[0][1] = new ItemStack(Blocks.STONE); + craftingGrid[1][1] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][2] = new ItemStack(Blocks.STONE); + craftingGrid[2][1] = new ItemStack(Blocks.STONE); + craftingResult = new ItemStack(WizardryBlocks.transportation_stone, 2); + this.renderCraftingTooltips(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[1][0] = new ItemStack(Items.STRING); + craftingGrid[0][1] = new ItemStack(Items.STRING); + craftingGrid[1][1] = new ItemStack(WizardryItems.magic_crystal); + craftingGrid[1][2] = new ItemStack(Items.STRING); + craftingGrid[2][1] = new ItemStack(Items.STRING); + craftingResult = new ItemStack(WizardryItems.magic_silk, 2); + this.renderCraftingTooltips(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + + }else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 2){ + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[1][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); + craftingResult = new ItemStack(WizardryItems.wizard_hat); + this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[1][1] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[0][2] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[1][2] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][2] = new ItemStack(WizardryItems.magic_silk); + craftingResult = new ItemStack(WizardryItems.wizard_robe); + this.renderCraftingTooltips(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[1][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[0][2] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][2] = new ItemStack(WizardryItems.magic_silk); + craftingResult = new ItemStack(WizardryItems.wizard_leggings); + this.renderCraftingTooltips(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); + craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); + craftingResult = new ItemStack(WizardryItems.wizard_boots); + this.renderCraftingTooltips(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + + }else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 3){ + + if(Wizardry.settings.useAlternateScrollRecipe){ + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(Items.PAPER); + craftingGrid[1][0] = new ItemStack(Items.STRING); + craftingGrid[2][0] = new ItemStack(WizardryItems.magic_crystal); + craftingResult = new ItemStack(WizardryItems.blank_scroll); + this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + }else{ + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(Items.PAPER); + craftingGrid[1][0] = new ItemStack(Items.STRING); + craftingResult = new ItemStack(WizardryItems.blank_scroll); + this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + } + + if(Wizardry.settings.firebombIsCraftable){ + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(Items.BLAZE_POWDER); + craftingGrid[1][0] = new ItemStack(Items.BLAZE_POWDER); + craftingGrid[0][1] = new ItemStack(Items.GLASS_BOTTLE); + craftingGrid[1][1] = new ItemStack(Items.GUNPOWDER); + craftingResult = new ItemStack(WizardryItems.firebomb, 3); + this.renderCraftingTooltips(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + } + + if(Wizardry.settings.poisonBombIsCraftable){ + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(Items.SPIDER_EYE); + craftingGrid[1][0] = new ItemStack(Items.SPIDER_EYE); + craftingGrid[0][1] = new ItemStack(Items.GLASS_BOTTLE); + craftingGrid[1][1] = new ItemStack(Items.GUNPOWDER); + craftingResult = new ItemStack(WizardryItems.poison_bomb, 3); + this.renderCraftingTooltips(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); + } + + if(Wizardry.settings.smokeBombIsCraftable){ + craftingGrid = new ItemStack[3][3]; + craftingGrid[0][0] = new ItemStack(Items.COAL); + craftingGrid[1][0] = new ItemStack(Items.COAL); + craftingGrid[0][1] = new ItemStack(Items.GLASS_BOTTLE); + craftingGrid[1][1] = new ItemStack(Items.GUNPOWDER); + craftingResult = new ItemStack(WizardryItems.smoke_bomb, 3); + this.renderCraftingTooltips(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); + } + + } + } + + private void renderCraftingRecipe(int xPos, int yPos, int mouseX, int mouseY, ItemStack[][] craftingGrid, ItemStack craftingResult) { + + GlStateManager.pushMatrix(); + RenderHelper.enableGUIStandardItemLighting(); + GlStateManager.disableLighting(); + GlStateManager.enableRescaleNormal(); + GL11.glEnable(GL11.GL_COLOR_MATERIAL); + GlStateManager.enableLighting(); + itemRender.zLevel = 100.0F; + + for(int i=0; i>(1); + sections = new ArrayList
    (1); + + BufferedReader bufferedreader = null; + + String textFilepath = "wizardry:texts/handbook_" + Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage().getLanguageCode() + ".txt"; + + try { + + bufferedreader = new BufferedReader(new InputStreamReader(this.mc.getResourceManager().getResource(new ResourceLocation(textFilepath)).getInputStream(), Charsets.UTF_8)); + + } catch (IOException e){ + + Wizardry.logger.info("Wizard handbook text file missing for the current language. Using default (English - US) instead."); + + textFilepath = "wizardry:texts/handbook_en_US.txt"; + + try { + + bufferedreader = new BufferedReader(new InputStreamReader(this.mc.getResourceManager().getResource(new ResourceLocation(textFilepath)).getInputStream(), Charsets.UTF_8)); + + } catch (IOException x){ + Wizardry.logger.error("Couldn't find file: wizardry:assets/texts/handbook_en_US.txt. The file may be" + + "missing; please try re-downloading and reinstalling Wizardry.", x); + } + } + + if(bufferedreader != null){ + + try { + + String paragraph = bufferedreader.readLine(); + ArrayList page = new ArrayList(1); + + int linesPerPage = 16; + + int lineNumber = 0; + + while(paragraph != null){ + + //System.out.println(paragraph); + + if(paragraph.contains("PAGEBREAK") || lineNumber >= linesPerPage){ + + text.add(page); + + page = new ArrayList(1); + + lineNumber = 0; + + if(paragraph.contains("PAGEBREAK")) paragraph = bufferedreader.readLine(); + + }else if(paragraph.contains("LINEBREAK")){ + + lineNumber++; + + page.add(""); + + paragraph = bufferedreader.readLine(); + + }else if(paragraph.contains("SECTION")){ + + sections.add(new Section(paragraph.replace("SECTION ", ""), text.size() + 1, this.width/2 + 7, + this.height/2 - this.ySize/2 + 14 + (sections.size()+2)*this.fontRendererObj.FONT_HEIGHT, nextButtonId++)); + paragraph = bufferedreader.readLine(); + + }else if(paragraph.contains("IMAGE")){ + + if(paragraph.contains("WORKBENCH")){ + this.guiPage = text.size() + 1; + }else if(paragraph.contains("CRYSTAL")){ + this.imagePage = text.size() + 1; + } + + paragraph = bufferedreader.readLine(); + + }else{ + + paragraph = paragraph.replaceAll("NEXT_SPELL_KEY", Keyboard.getKeyName(ClientProxy.NEXT_SPELL.getKeyCode())); + paragraph = paragraph.replaceAll("PREVIOUS_SPELL_KEY", Keyboard.getKeyName(ClientProxy.PREVIOUS_SPELL.getKeyCode())); + paragraph = paragraph.replaceAll("MANA_PER_CRYSTAL_MINUS_30", "" + (Constants.MANA_PER_CRYSTAL - 30)); + paragraph = paragraph.replaceAll("MANA_PER_CRYSTAL", "" + Constants.MANA_PER_CRYSTAL); + paragraph = paragraph.replaceAll("BASIC_MAX_CHARGE", "" + Tier.BASIC.maxCharge); + paragraph = paragraph.replaceAll("APPRENTICE_MAX_CHARGE", "" + Tier.APPRENTICE.maxCharge); + paragraph = paragraph.replaceAll("ADVANCED_MAX_CHARGE", "" + Tier.ADVANCED.maxCharge); + paragraph = paragraph.replaceAll("MASTER_MAX_CHARGE", "" + Tier.MASTER.maxCharge); + paragraph = paragraph.replaceAll("BASIC_COLOUR", "\u00A77"); + paragraph = paragraph.replaceAll("APPRENTICE_COLOUR", Tier.APPRENTICE.getFormattingCode()); + paragraph = paragraph.replaceAll("ADVANCED_COLOUR", Tier.ADVANCED.getFormattingCode()); + paragraph = paragraph.replaceAll("MASTER_COLOUR", Tier.MASTER.getFormattingCode()); + paragraph = paragraph.replaceAll("FIRE_COLOUR", Element.FIRE.getFormattingCode()); + paragraph = paragraph.replaceAll("ICE_COLOUR", Element.ICE.getFormattingCode()); + paragraph = paragraph.replaceAll("LIGHTNING_COLOUR", Element.LIGHTNING.getFormattingCode()); + paragraph = paragraph.replaceAll("NECROMANCY_COLOUR", Element.NECROMANCY.getFormattingCode()); + paragraph = paragraph.replaceAll("EARTH_COLOUR", Element.EARTH.getFormattingCode()); + paragraph = paragraph.replaceAll("SORCERY_COLOUR", Element.SORCERY.getFormattingCode()); + paragraph = paragraph.replaceAll("HEALING_COLOUR", Element.HEALING.getFormattingCode()); + paragraph = paragraph.replaceAll("RESET_COLOUR", "\u00A70"); + paragraph = paragraph.replaceAll("VERSION", Wizardry.VERSION); + + int linesInParagraph = this.fontRendererObj.listFormattedStringToWidth(paragraph, GuiWizardHandbook.PAGE_WIDTH).size(); + + // Ignores empty lines at the top of a page. + if(paragraph.isEmpty() && lineNumber == 0){ + + paragraph = bufferedreader.readLine(); + + // Normal paragraph, all on one page + }else if(lineNumber + linesInParagraph <= linesPerPage){ + + page.add(paragraph); + + lineNumber += linesInParagraph; + + paragraph = bufferedreader.readLine(); + + // Paragraphs split across two pages (or more?) + }else{ + + int linesInFirstPart = linesPerPage - lineNumber; + + String paragraphFirstPart = ""; + String paragraphLastPart = ""; + + int i = 0; + + List strings = this.fontRendererObj.listFormattedStringToWidth(paragraph, GuiWizardHandbook.PAGE_WIDTH); + + for(Object s : strings){ + if(i < linesInFirstPart){ + paragraphFirstPart = paragraphFirstPart.concat((String)s + " "); + }else{ + paragraphLastPart = paragraphLastPart.concat((String)s + " "); + } + i++; + } + + //System.out.println("Paragraph crosses page boundary; string split into: \"" + paragraphFirstPart + "\" and \"" + paragraphLastPart + "\""); + + page.add(paragraphFirstPart); + + lineNumber += linesInFirstPart; + + paragraph = paragraphLastPart; + } + } + } + + text.add(page); + + } catch (IOException e){ + Wizardry.logger.error("Something went wrong reading file: " + textFilepath + ". The file may be damaged;" + + "please try re-downloading and reinstalling wizardry.", e); + } + } + } + + private class Section { + + /** The integer text colour used for the section when it is moused over. Currently orange. */ + private static final int HIGHLIGHT_COLOUR = 0xdd4c1d; + + String name; + int pageNumber; + int x, y; + int buttonId; + + Section(String name, int pageNumber, int x, int y, int id){ + this.name = name; + this.pageNumber = pageNumber; + this.x = x; + this.y = y; + this.buttonId = id; + GuiWizardHandbook.this.buttonList.add(new GuiButtonInvisible(id, x, y, GuiWizardHandbook.PAGE_WIDTH, GuiWizardHandbook.this.fontRendererObj.FONT_HEIGHT)); + } + + void hideButton(){ + GuiWizardHandbook.this.buttonList.get(buttonId).visible = false; + } + + void drawContents(){ + + GuiWizardHandbook.this.buttonList.get(buttonId).visible = true; + + GuiWizardHandbook.this.fontRendererObj.drawString(name, x, y, GuiWizardHandbook.this.buttonList.get(buttonId).isMouseOver() ? HIGHLIGHT_COLOUR : BLACK); + + int nameWidth = GuiWizardHandbook.this.fontRendererObj.getStringWidth(name); + + String dotsAndNumber = " " + this.pageNumber; + + while(GuiWizardHandbook.this.fontRendererObj.getStringWidth(dotsAndNumber) < GuiWizardHandbook.PAGE_WIDTH - nameWidth - 2){ + dotsAndNumber = "." + dotsAndNumber; + } + + GuiWizardHandbook.this.fontRendererObj.drawString(dotsAndNumber, x + GuiWizardHandbook.PAGE_WIDTH - GuiWizardHandbook.this.fontRendererObj.getStringWidth(dotsAndNumber), y, BLACK); + } + } + + @Override + public void onGuiClosed() + { + super.onGuiClosed(); + Keyboard.enableRepeatEvents(false); + } + + /** + * Fired when a control is clicked. This is the equivalent of ActionListener.actionPerformed(ActionEvent e). + */ + @Override + protected void actionPerformed(GuiButton par1GuiButton){ + + if(par1GuiButton.enabled){ + if(par1GuiButton.id == 0){ + if(pageNumber < (text.size()-1)/2) pageNumber++; + }else if(par1GuiButton.id == 1){ + if(pageNumber > 0) pageNumber--; + }else{ + if(pageNumber == 1) pageNumber = (sections.get(par1GuiButton.id - 2).pageNumber-1)/2; + } + } + } + + /** + * Args: left, top, width, height, pointX, pointY. Note: left, top are local to Gui, pointX, pointY are local to + * screen + */ + protected boolean isPointInRegion(int par1, int par2, int par3, int par4, int par5, int par6) + { + int k1 = this.width/2 - xSize/2; + int l1 = this.height/2 - this.ySize/2; + par5 -= k1; + par6 -= l1; + return par5 >= par1 - 1 && par5 < par1 + par3 + 1 && par6 >= par2 - 1 && par6 < par2 + par4 + 1; + } + + /** + * Draws a textured rectangle, stretching the section of the image to fit the size given. + * @param x The x position of the rectangle + * @param y The y position of the rectangle + * @param u The x position of the top left corner of the section of the image wanted, expressed as a fraction of the image width + * @param v The y position of the top left corner of the section of the image wanted, expressed as a fraction of the image width + * @param finalWidth The width as rendered + * @param finalHeight The height as rendered + * @param width The width of the section, expressed as a fraction of the image width + * @param height The height of the section, expressed as a fraction of the image width + */ + public static void drawTexturedStretchedRect(int x, int y, int u, int v, int finalWidth, int finalHeight, int width, int height){ + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + buffer.pos((x), y + finalHeight, 0).tex(u, v + height).endVertex(); + buffer.pos(x + finalWidth, y + finalHeight, 0).tex(u + width, v + height).endVertex(); + buffer.pos(x + finalWidth, (y), 0).tex(u + width, v).endVertex(); + buffer.pos((x), (y), 0).tex(u, v).endVertex(); + tessellator.draw(); + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/MixedFontRenderer.java b/src/main/java/electroblob/wizardry/client/MixedFontRenderer.java new file mode 100644 index 00000000..43b51f8b --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/MixedFontRenderer.java @@ -0,0 +1,94 @@ +package electroblob.wizardry.client; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.client.settings.GameSettings; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** Font renderer that renders parts of strings surrounded by '#' (without quotes) in the SGA instead of normal text. + * @since Wizardry 1.1 */ +@SideOnly(Side.CLIENT) +public class MixedFontRenderer extends FontRenderer { + + public MixedFontRenderer(GameSettings p_i1035_1_, ResourceLocation p_i1035_2_, TextureManager p_i1035_3_, + boolean p_i1035_4_) { + super(p_i1035_1_, p_i1035_2_, p_i1035_3_, p_i1035_4_); + } + + @Override + public int drawString(String string, float x, float y, int colour, boolean shadow){ + + int l = 0; + + boolean sga = false; + + while(string.indexOf('#') > -1){ + + String section = string.substring(0, string.indexOf('#')); + + if(sga){ + l += Minecraft.getMinecraft().standardGalacticFontRenderer.drawString(section, x, y, colour, shadow); + x += Minecraft.getMinecraft().standardGalacticFontRenderer.getStringWidth(section); + }else{ + l += Minecraft.getMinecraft().fontRendererObj.drawString(section, x, y, colour, shadow); + x += Minecraft.getMinecraft().fontRendererObj.getStringWidth(section); + } + + string = string.substring(string.indexOf('#') + 1); + sga = !sga; + } + + if(sga){ + l += Minecraft.getMinecraft().standardGalacticFontRenderer.drawString(string, x, y, colour, shadow); + }else{ + l += Minecraft.getMinecraft().fontRendererObj.drawString(string, x, y, colour, shadow); + } + + return l; + } + + @Override + public int getStringWidth(String string){ + + int l = 0; + + boolean sga = false; + + while(string.indexOf('#') > -1){ + + String section = string.substring(0, string.indexOf('#')); + + if(sga){ + l += Minecraft.getMinecraft().standardGalacticFontRenderer.getStringWidth(section); + }else{ + l += Minecraft.getMinecraft().fontRendererObj.getStringWidth(section); + } + + string = string.substring(string.indexOf('#') + 1); + sga = !sga; + } + + if(sga){ + l += Minecraft.getMinecraft().standardGalacticFontRenderer.getStringWidth(string); + }else{ + l += Minecraft.getMinecraft().fontRendererObj.getStringWidth(string); + } + + return l; + } + + // This doesn't work the same way yet + @Override + public void drawSplitString(String string, int x, int y, int width, int colour){ + + if(string.contains("#")){ + Minecraft.getMinecraft().standardGalacticFontRenderer.drawSplitString(string.substring(1), x, y, width, colour); + }else{ + Minecraft.getMinecraft().fontRendererObj.drawSplitString(string, x, y, width, colour); + } + } + +} diff --git a/src/main/java/electroblob/wizardry/client/MovingSoundEntity.java b/src/main/java/electroblob/wizardry/client/MovingSoundEntity.java new file mode 100644 index 00000000..3696e866 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/MovingSoundEntity.java @@ -0,0 +1,59 @@ +package electroblob.wizardry.client; + +import net.minecraft.client.audio.MovingSound; +import net.minecraft.entity.Entity; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvent; +import net.minecraft.util.math.MathHelper; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +// Copied from MovingSoundMinecart; if it ever breaks between updates take a look at that. +@SideOnly(Side.CLIENT) +public class MovingSoundEntity extends MovingSound +{ + private final Entity source; + private float distance = 0.0F; + + public MovingSoundEntity(Entity entity, SoundEvent sound, float volume, float pitch, boolean repeat) + { + // Uses BLOCKS because that's the closest thing to inanimate entities. Could use NEUTRAL like MovingSoundMinecart. + super(sound, SoundCategory.BLOCKS); + this.source = entity; + this.repeat = repeat; + this.volume = volume; + this.pitch = pitch; + this.repeatDelay = 0; + } + + /** + * Updates the JList with a new model. + */ + @Override + public void update() + { + if (this.source.isDead && repeat) + { + this.donePlaying = true; + } + else + { + this.xPosF = (float)this.source.posX; + this.yPosF = (float)this.source.posY; + this.zPosF = (float)this.source.posZ; + float f = MathHelper.sqrt_double(this.source.motionX * this.source.motionX + this.source.motionY * this.source.motionY + this.source.motionZ * this.source.motionZ); + + // Is this something to do with the Doppler effect? + if ((double)f >= 0.01D) + { + this.distance = MathHelper.clamp_float(this.distance + 0.0025F, 0.0F, 1.0F); + this.volume = 0.0F + MathHelper.clamp_float(f, 0.0F, 0.5F) * 0.7F; + } + else + { + //this.pitch = 0.0F; + //this.volume = 0.0F; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java b/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java new file mode 100644 index 00000000..84b0d552 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java @@ -0,0 +1,752 @@ +package electroblob.wizardry.client; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.item.ItemSpectralBow; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.packet.PacketControlInput; +import electroblob.wizardry.packet.WizardryPacketHandler; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.spell.Flight; +import electroblob.wizardry.spell.ShadowWard; +import electroblob.wizardry.spell.Shield; +import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; +import electroblob.wizardry.util.WandHelper; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.RayTraceResult; +import net.minecraftforge.client.event.FOVUpdateEvent; +import net.minecraftforge.client.event.MouseEvent; +import net.minecraftforge.client.event.RenderGameOverlayEvent; +import net.minecraftforge.client.event.RenderLivingEvent; +import net.minecraftforge.client.event.RenderPlayerEvent; +import net.minecraftforge.client.event.RenderWorldLastEvent; +import net.minecraftforge.client.event.TextureStitchEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.relauncher.Side; + +/** + * Event handler responsible for all client-side only events, mostly rendering. + * @author Electroblob + * @since Wizardry 1.0 + */ +@Mod.EventBusSubscriber(Side.CLIENT) +public final class WizardryClientEventHandler { + + private static final ResourceLocation shieldTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/shield.png"); + private static final ResourceLocation wingTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/wing.png"); + private static final ResourceLocation shadowWardTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/shadow_ward.png"); + private static final ResourceLocation sixthSenseTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/sixth_sense.png"); + private static final ResourceLocation sixthSenseOverlayTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/sixth_sense_overlay.png"); + private static final ResourceLocation frostOverlayTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/frost_overlay.png"); + private static final ResourceLocation pointerTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/pointer.png"); + private static final ResourceLocation targetPointerTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/target_pointer.png"); + + @SubscribeEvent + public static void onTextureStitchEvent(TextureStitchEvent.Pre event){ + event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_CRYSTAL); + event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_UPGRADE); + } + + // Shift-scrolling to change spells + @SubscribeEvent + public static void onMouseEvent(MouseEvent event){ + + EntityPlayer player = Minecraft.getMinecraft().thePlayer; + ItemStack wand = player.getHeldItemMainhand(); + + if(wand == null || !(wand.getItem() instanceof ItemWand)){ + wand = player.getHeldItemOffhand(); + // If the player isn't holding a wand, then nothing else needs to be done. + if(wand == null || !(wand.getItem() instanceof ItemWand)) return; + } + + if(Minecraft.getMinecraft().inGameHasFocus && wand != null && event.getDwheel() != 0 && player.isSneaking() + && Wizardry.settings.enableShiftScrolling){ + + event.setCanceled(true); + + if(event.getDwheel() > 0){ + // Packet building + IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.PREVIOUS_SPELL_KEY); + WizardryPacketHandler.net.sendToServer(msg); + + }else if(event.getDwheel() < 0){ + // Packet building + IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.NEXT_SPELL_KEY); + WizardryPacketHandler.net.sendToServer(msg); + } + } + } + + @SubscribeEvent + public static void onFOVUpdateEvent(FOVUpdateEvent event){ + + // Bow zoom. Taken directly from AbstractClientPlayer so it works exactly like vanilla. + if(event.getEntity().isHandActive() && event.getEntity().getActiveItemStack() != null + && event.getEntity().getActiveItemStack().getItem() instanceof ItemSpectralBow){ + + int maxUseTicks = event.getEntity().getItemInUseMaxCount(); + + float maxUseSeconds = (float)maxUseTicks / 20.0F; + + if(maxUseSeconds > 1.0F){ + maxUseSeconds = 1.0F; + }else{ + maxUseSeconds = maxUseSeconds * maxUseSeconds; + } + + event.setNewfov(event.getFov() * 1.0F - maxUseSeconds * 0.15F); + } + } + + // Third person + @SubscribeEvent + public static void onRenderPlayerEvent(RenderPlayerEvent.Post event){ + renderShieldIfActive(event.getEntityPlayer()); + renderWingsIfActive(event.getEntityPlayer(), event.getPartialRenderTick()); + renderShadowWardIfActive(event.getEntityPlayer()); + } + + // First person + @SubscribeEvent + public static void onRenderWorldLastEvent(RenderWorldLastEvent event){ + // Now only fires in first person. + if(Minecraft.getMinecraft().gameSettings.thirdPersonView == 0){ + renderShieldFirstPerson(Minecraft.getMinecraft().thePlayer); + renderShadowWardFirstPerson(Minecraft.getMinecraft().thePlayer); + } + } + + @SubscribeEvent + public static void onRenderLivingEvent(RenderLivingEvent.Post event){ + /* + // Frost effect + if(event.entity.isPotionActive(Wizardry.frost)){ + + GlStateManager.pushMatrix(); + GL11.glDisable(GL11.GL_TEXTURE_2D); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + float someScalingFactor = 0.0625f; + + float yaw = event.entity.prevRotationYaw; + + //int brightness = event.entity.getBrightnessForRender(0); + + //int j = brightness % 65536; + //int k = brightness / 65536; + //OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)j / 1.0F, (float)k / 1.0F); + //GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + + + GlStateManager.translate(event.x, event.y, event.z); + + GlStateManager.rotate(-yaw + 180, 0F, 1F, 0F); + + Render render = renderManager.getEntityRenderObject(event.entity); + + RenderLiving renderliving = event.renderer; + + // Reflection + + try { + + Timer timer = ReflectionHelper.getPrivateValue(Minecraft.class, Minecraft.getMinecraft(), "timer"); + + // Chooses the appropriate zombie model, normal or villager + // Fixed by moving before the model fields are accessed + if(render instanceof RenderZombie && event.entity instanceof EntityZombie){ + // The second argument is never used... + ReflectionHelper.findMethod(RenderZombie.class, (RenderZombie)render, new String[]{"func_82427_a"}, EntityZombie.class) + .invoke(renderliving, (EntityZombie)event.entity); + } + + // Turns out that java automatically infers the type parameter T in this method from the type + // I am assigning the returned value to. Neat! + ModelBase mainModel = ReflectionHelper.getPrivateValue(RenderLiving.class, renderliving, "mainModel"); + + mainModel.isRiding = event.entity.isRiding(); + mainModel.isChild = event.entity.isChild(); + + GlStateManager.enableRescaleNormal(); + GlStateManager.scale(-1.0F, -1.0F, 1.0F); + + // The second argument is never used... + ReflectionHelper.findMethod(RenderLiving.class, renderliving, new String[]{"preRenderCallback"}, EntityLivingBase.class, float.class) + .invoke(renderliving, event.entity, someScalingFactor); + + // Why is this -1.5f? No idea! + GlStateManager.translate(0, -1.5f, 0); + + float f6 = event.entity.prevLimbSwingAmount + (event.entity.limbSwingAmount - event.entity.prevLimbSwingAmount) * timer.renderPartialTicks; + float f7 = event.entity.limbSwing - event.entity.limbSwingAmount * (1.0F - timer.renderPartialTicks); + + if (event.entity.isChild()) + { + f7 *= 3.0F; + } + + if (f6 > 1.0F) + { + f6 = 1.0F; + } + + mainModel.setLivingAnimations(event.entity, f7, f6, timer.renderPartialTicks); + + GlStateManager.enableAlpha(); + GlStateManager.color(0.5f, 0.7f, 1, 0.5f); + + mainModel.render(event.entity, f7, f6, 0, 0, 0, someScalingFactor); + + GL11.glDepthMask(true); + + // 'Pokemon' exception handling... Because why not?! + } catch (Exception e) { + System.err.println("Something went very wrong! Error while rendering frost effect:"); + e.printStackTrace(); + } + + GlStateManager.disableAlpha(); + GlStateManager.disableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GlStateManager.disableRescaleNormal(); + GL11.glEnable(GL11.GL_TEXTURE_2D); + GlStateManager.popMatrix(); + } + */ + + Minecraft mc = Minecraft.getMinecraft(); + WizardData properties = WizardData.get(mc.thePlayer); + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(mc.theWorld, mc.thePlayer, 16); + RenderManager renderManager = event.getRenderer().getRenderManager(); + + ItemStack wand = mc.thePlayer.getHeldItemMainhand(); + + if(wand == null || !(wand.getItem() instanceof ItemWand)){ + wand = mc.thePlayer.getHeldItemOffhand(); + } + + // Target selection pointer + if(mc.thePlayer.isSneaking() && wand != null && wand.getItem() instanceof ItemWand && rayTrace != null + && rayTrace.entityHit instanceof EntityLivingBase && rayTrace.entityHit == event.getEntity() + && properties != null && properties.selectedMinion != null){ + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + + GlStateManager.pushMatrix(); + + GlStateManager.disableCull(); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + // Disabling depth test allows it to be seen through everything. + GL11.glDisable(GL11.GL_DEPTH_TEST); + GlStateManager.color(1, 1, 1, 1); + + GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height + 0.5, event.getZ()); + + // This counteracts the reverse rotation behaviour when in front f5 view. + // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. + float yaw = mc.gameSettings.thirdPersonView == 2 ? renderManager.playerViewX : -renderManager.playerViewX; + GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F); + GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + mc.renderEngine.bindTexture(targetPointerTexture); + + buffer.pos(-0.2, 0.24, 0).tex(0, 0).endVertex(); + buffer.pos(0.2, 0.24, 0).tex(9f/16f, 0).endVertex(); + buffer.pos(0.2, -0.24, 0).tex(9f/16f, 11f/16f).endVertex(); + buffer.pos(-0.2, -0.24, 0).tex(0, 11f/16f).endVertex(); + + tessellator.draw(); + + GlStateManager.enableCull(); + GlStateManager.enableLighting(); + GL11.glEnable(GL11.GL_DEPTH_TEST); + + GlStateManager.popMatrix(); + } + + // Summoned creature selection pointer + if(properties != null && properties.selectedMinion != null && properties.selectedMinion.get() == event.getEntity()){ + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + + GlStateManager.pushMatrix(); + + GlStateManager.disableCull(); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + // Disabling depth test allows it to be seen through everything. + GL11.glDisable(GL11.GL_DEPTH_TEST); + GlStateManager.color(1, 1, 1, 1); + + GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height + 0.5, event.getZ()); + + // This counteracts the reverse rotation behaviour when in front f5 view. + // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. + float yaw = mc.gameSettings.thirdPersonView == 2 ? renderManager.playerViewX : -renderManager.playerViewX; + GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F); + GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + mc.renderEngine.bindTexture(pointerTexture); + + buffer.pos(-0.2, 0.24, 0).tex(0, 0).endVertex(); + buffer.pos(0.2, 0.24, 0).tex(9f/16f, 0).endVertex(); + buffer.pos(0.2, -0.24, 0).tex(9f/16f, 11f/16f).endVertex(); + buffer.pos(-0.2, -0.24, 0).tex(0, 11f/16f).endVertex(); + + tessellator.draw(); + + GlStateManager.enableCull(); + GlStateManager.enableLighting(); + GL11.glEnable(GL11.GL_DEPTH_TEST); + + GlStateManager.popMatrix(); + } + + // Sixth sense + if(mc.thePlayer.isPotionActive(WizardryPotions.sixth_sense) && event.getEntity() != mc.thePlayer + && mc.thePlayer.getActivePotionEffect(WizardryPotions.sixth_sense) != null + && event.getEntity().getDistanceToEntity(mc.thePlayer) < 20*(1+mc.thePlayer.getActivePotionEffect(WizardryPotions.sixth_sense).getAmplifier()*Constants.RANGE_INCREASE_PER_LEVEL)){ + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + + GlStateManager.pushMatrix(); + + GlStateManager.disableCull(); + GlStateManager.enableBlend(); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + // Disabling depth test allows it to be seen through everything. + GL11.glDisable(GL11.GL_DEPTH_TEST); + + GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height * 0.6, event.getZ()); + + // This counteracts the reverse rotation behaviour when in front f5 view. + // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. + float yaw = mc.gameSettings.thirdPersonView == 2 ? renderManager.playerViewX : -renderManager.playerViewX; + GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F); + GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + mc.renderEngine.bindTexture(sixthSenseTexture); + + buffer.pos(-0.6, 0.6, 0).tex(0, 0).endVertex(); + buffer.pos(0.6, 0.6, 0).tex(1, 0).endVertex(); + buffer.pos(0.6, -0.6, 0).tex(1, 1).endVertex(); + buffer.pos(-0.6, -0.6, 0).tex(0, 1).endVertex(); + + tessellator.draw(); + + GlStateManager.enableCull(); + GlStateManager.disableBlend(); + GlStateManager.enableLighting(); + GL11.glEnable(GL11.GL_DEPTH_TEST); + + GlStateManager.popMatrix(); + } + } + + @SubscribeEvent + public static void onRenderGameOverlayEvent(RenderGameOverlayEvent.Post event){ + + if(event.getType() == RenderGameOverlayEvent.ElementType.HELMET + && Minecraft.getMinecraft().thePlayer.isPotionActive(WizardryPotions.sixth_sense)){ + + GlStateManager.pushMatrix(); + + GL11.glDisable(GL11.GL_DEPTH_TEST); + GL11.glDepthMask(false); + OpenGlHelper.glBlendFunc(770, 771, 1, 0); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GlStateManager.disableAlpha(); + Minecraft.getMinecraft().renderEngine.bindTexture(sixthSenseOverlayTexture); + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + buffer.pos(0.0D, (double)event.getResolution().getScaledHeight(), -90.0D).tex(0.0D, 1.0D).endVertex(); + buffer.pos((double)event.getResolution().getScaledWidth(), (double)event.getResolution().getScaledHeight(), -90.0D).tex(1.0D, 1.0D).endVertex(); + buffer.pos((double)event.getResolution().getScaledWidth(), 0.0D, -90.0D).tex(1.0D, 0.0D).endVertex(); + buffer.pos(0.0D, 0.0D, -90.0D).tex(0.0D, 0.0D).endVertex(); + tessellator.draw(); + + GL11.glDepthMask(true); + GL11.glEnable(GL11.GL_DEPTH_TEST); + GlStateManager.enableAlpha(); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + + GlStateManager.popMatrix(); + } + + if(event.getType() == RenderGameOverlayEvent.ElementType.HELMET + && Minecraft.getMinecraft().thePlayer.isPotionActive(WizardryPotions.frost)){ + + GlStateManager.pushMatrix(); + + GL11.glDisable(GL11.GL_DEPTH_TEST); + GL11.glDepthMask(false); + OpenGlHelper.glBlendFunc(770, 771, 1, 0); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GlStateManager.disableAlpha(); + Minecraft.getMinecraft().renderEngine.bindTexture(frostOverlayTexture); + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + buffer.pos(0.0D, (double)event.getResolution().getScaledHeight(), -90.0D).tex(0.0D, 1.0D).endVertex(); + buffer.pos((double)event.getResolution().getScaledWidth(), (double)event.getResolution().getScaledHeight(), -90.0D).tex(1.0D, 1.0D).endVertex(); + buffer.pos((double)event.getResolution().getScaledWidth(), 0.0D, -90.0D).tex(1.0D, 0.0D).endVertex(); + buffer.pos(0.0D, 0.0D, -90.0D).tex(0.0D, 0.0D).endVertex(); + + tessellator.draw(); + GL11.glDepthMask(true); + GL11.glEnable(GL11.GL_DEPTH_TEST); + GlStateManager.enableAlpha(); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + + GlStateManager.popMatrix(); + } + } + + // FIXME: Something in here is making the first person shadow ward rather translucent. + private static void renderShadowWardFirstPerson(EntityPlayer entityplayer){ + ItemStack wand = entityplayer.getActiveItemStack(); + if(WizardData.get(entityplayer) != null && WizardData.get(entityplayer).currentlyCasting() instanceof ShadowWard || (entityplayer.isHandActive() && wand != null && wand.getItemDamage() < wand.getMaxDamage() + && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof ShadowWard)){ + + GlStateManager.pushMatrix(); + + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GlStateManager.shadeModel(GL11.GL_SMOOTH); + GlStateManager.disableLighting(); + GlStateManager.disableAlpha(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + + GlStateManager.translate(0, 1.2, 0); + GlStateManager.rotate(-entityplayer.rotationYaw, 0, 1, 0); + GlStateManager.rotate(entityplayer.rotationPitch, 1, 0, 0); + + Minecraft.getMinecraft().renderEngine.bindTexture(shadowWardTexture); + + GlStateManager.pushMatrix(); + + GlStateManager.translate(0, 0, 1.2); + GlStateManager.rotate(entityplayer.worldObj.getWorldTime()*-2, 0, 0, 1); + GlStateManager.scale(1.1, 1.1, 1.1); + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex(); + buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex(); + buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex(); + buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex(); + + tessellator.draw(); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex(); + buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex(); + buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex(); + buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex(); + + tessellator.draw(); + + GlStateManager.popMatrix(); + + GlStateManager.shadeModel(GL11.GL_FLAT); + GlStateManager.enableLighting(); + GlStateManager.disableBlend(); + + GlStateManager.popMatrix(); + + } + } + + private static void renderShadowWardIfActive(EntityPlayer entityplayer){ + ItemStack wand = entityplayer.getActiveItemStack(); + if(WizardData.get(entityplayer).currentlyCasting() instanceof ShadowWard || (entityplayer.isHandActive() && wand != null + && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof ShadowWard)){ + + GlStateManager.pushMatrix(); + + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + + GlStateManager.rotate(180, 0, 1, 0); + GlStateManager.rotate(-entityplayer.renderYawOffset, 0, 1, 0); + + Minecraft.getMinecraft().renderEngine.bindTexture(shadowWardTexture); + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + + GlStateManager.translate(0, 1.2, 0); + GlStateManager.rotate(entityplayer.worldObj.getWorldTime()*-2, 0, 0, 1); + GlStateManager.scale(1.1, 1.1, 1.1); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex(); + buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex(); + buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex(); + buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex(); + + tessellator.draw(); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex(); + buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex(); + buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex(); + buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex(); + + tessellator.draw(); + + GlStateManager.enableLighting(); + GlStateManager.disableBlend(); + + GlStateManager.popMatrix(); + + } + } + + private static void renderWingsIfActive(EntityPlayer entityplayer, float partialTickTime){ + ItemStack wand = entityplayer.getActiveItemStack(); + if(WizardData.get(entityplayer).currentlyCasting() instanceof Flight || (entityplayer.isHandActive() && wand != null + && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof Flight)){ + + GlStateManager.pushMatrix(); + + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + + //GlStateManager.rotate(-entityplayer.rotationYawHead, 0, 1, 0); + GlStateManager.rotate(-entityplayer.renderYawOffset, 0, 1, 0); + //GlStateManager.rotate(180, 1, 0, 0); + + Minecraft.getMinecraft().renderEngine.bindTexture(wingTexture); + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + + GlStateManager.pushMatrix(); + + GlStateManager.translate(0.1, 0.4, -0.15); + GlStateManager.rotate(20 + 20*(float)Math.sin(entityplayer.worldObj.getWorldTime()*0.3), 0, 1, 0); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + buffer.pos(0, 2, 0).tex(0, 0).endVertex(); + buffer.pos(2, 2, 0).tex(1, 0).endVertex(); + buffer.pos(2, 0, 0).tex(1, 1).endVertex(); + buffer.pos(0, 0, 0).tex(0, 1).endVertex(); + + tessellator.draw(); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + buffer.pos(0, 2, 0).tex(0, 0).endVertex(); + buffer.pos(0, 0, 0).tex(0, 1).endVertex(); + buffer.pos(2, 0, 0).tex(1, 1).endVertex(); + buffer.pos(2, 2, 0).tex(1, 0).endVertex(); + + tessellator.draw(); + + GlStateManager.popMatrix(); + + GlStateManager.pushMatrix(); + + GlStateManager.translate(-0.1, 0.4, -0.15); + GlStateManager.rotate(-200 - 20*(float)Math.sin(entityplayer.worldObj.getWorldTime()*0.3), 0, 1, 0); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + buffer.pos(0, 2, 0).tex(0, 0).endVertex(); + buffer.pos(2, 2, 0).tex(1, 0).endVertex(); + buffer.pos(2, 0, 0).tex(1, 1).endVertex(); + buffer.pos(0, 0, 0).tex(0, 1).endVertex(); + + tessellator.draw(); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + buffer.pos(0, 2, 0).tex(0, 0).endVertex(); + buffer.pos(0, 0, 0).tex(0, 1).endVertex(); + buffer.pos(2, 0, 0).tex(1, 1).endVertex(); + buffer.pos(2, 2, 0).tex(1, 0).endVertex(); + + tessellator.draw(); + + GlStateManager.popMatrix(); + + GlStateManager.enableLighting(); + GlStateManager.disableBlend(); + + GlStateManager.popMatrix(); + } + } + + private static void renderShieldFirstPerson(EntityPlayer entityplayer){ + ItemStack wand = entityplayer.getActiveItemStack(); + if(WizardData.get(entityplayer) != null && WizardData.get(entityplayer).shield != null && (WizardData.get(entityplayer).currentlyCasting() instanceof Shield || (entityplayer.isHandActive() && wand != null + && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof Shield))){ + + GlStateManager.pushMatrix(); + + GlStateManager.disableCull(); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_SRC_ALPHA); + GlStateManager.shadeModel(GL11.GL_SMOOTH); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + + GlStateManager.translate(0, 1.4, 0); + + GlStateManager.rotate(-entityplayer.rotationYaw, 0, 1, 0); + GlStateManager.rotate(entityplayer.rotationPitch, 1, 0, 0); + + GlStateManager.translate(0, 0, 0.8); + + Tessellator tessellator = Tessellator.getInstance(); + + Minecraft.getMinecraft().renderEngine.bindTexture(shieldTexture); + + renderShield(tessellator); + + GlStateManager.enableLighting(); + + GlStateManager.shadeModel(GL11.GL_FLAT); + GlStateManager.enableCull(); + GlStateManager.disableBlend(); + //RenderHelper.enableStandardItemLighting(); + + GlStateManager.popMatrix(); + } + } + + private static void renderShieldIfActive(EntityPlayer entityplayer){ + ItemStack wand = entityplayer.getActiveItemStack(); + if(WizardData.get(entityplayer).shield != null && (WizardData.get(entityplayer).currentlyCasting() instanceof Shield || (entityplayer.isHandActive() && wand != null + && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof Shield))){ + + GlStateManager.pushMatrix(); + + GlStateManager.disableCull(); + GlStateManager.enableBlend(); + // For some reason, the old blend function (GL11.GL_SRC_ALPHA, GL11.GL_SRC_ALPHA) caused the inner + // edges to appear black, so I have changed it to this, which looks very slightly different. + GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_SRC_ALPHA); + GlStateManager.shadeModel(GL11.GL_SMOOTH); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + + GlStateManager.translate(0, 1.3, 0); + + //GlStateManager.rotate(180, 0, 1, 0); + GlStateManager.rotate(-entityplayer.renderYawOffset, 0, 1, 0); + //GlStateManager.rotate(-entityplayer.rotationPitch, 1, 0, 0); + + GlStateManager.translate(0, 0, 0.8); + + Tessellator tessellator = Tessellator.getInstance(); + + Minecraft.getMinecraft().renderEngine.bindTexture(shieldTexture); + + renderShield(tessellator); + + GlStateManager.enableLighting(); + + GlStateManager.shadeModel(GL11.GL_FLAT); + GlStateManager.enableCull(); + GlStateManager.disableBlend(); + //RenderHelper.enableStandardItemLighting(); + + GlStateManager.popMatrix(); + } + } + + private static void renderShield(Tessellator tessellator){ + + VertexBuffer buffer = tessellator.getBuffer(); + + double widthOuter = 0.6d; + double heightOuter = 0.7d; + double widthInner = 0.3d; + double heightInner = 0.4d; + double depth = 0.2d; + + buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR); + + buffer.pos(-widthOuter, heightInner, -depth).tex(0, 0.2).color(0, 0, 0, 255).endVertex(); + buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex(); + buffer.pos(-widthInner, heightOuter, -depth).tex(0.2, 0).color(0, 0, 0, 255).endVertex(); + buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex(); + + buffer.pos(widthInner, heightOuter, -depth).tex(0.8, 0).color(0, 0, 0, 255).endVertex(); + buffer.pos(widthInner, heightInner, 0).tex(0.8, 0.2).color(200, 200, 255, 255).endVertex(); + buffer.pos(widthOuter, heightInner, -depth).tex(1, 0.2).color(0, 0, 0, 255).endVertex(); + buffer.pos(widthInner, heightInner, 0).tex(0.8, 0.2).color(200, 200, 255, 255).endVertex(); + + buffer.pos(widthOuter, -heightInner, -depth).tex(1, 0.8).color(0, 0, 0, 255).endVertex(); + buffer.pos(widthInner, -heightInner, 0).tex(0.8, 0.8).color(200, 200, 255, 255).endVertex(); + buffer.pos(widthInner, -heightOuter, -depth).tex(0.8, 1).color(0, 0, 0, 255).endVertex(); + buffer.pos(widthInner, -heightInner, 0).tex(0.8, 0.8).color(200, 200, 255, 255).endVertex(); + + buffer.pos(-widthInner, -heightOuter, -depth).tex(0.2, 1).color(0, 0, 0, 255).endVertex(); + buffer.pos(-widthInner, -heightInner, 0).tex(0.2, 0.8).color(200, 200, 255, 255).endVertex(); + buffer.pos(-widthOuter, -heightInner, -depth).tex(0, 0.8).color(0, 0, 0, 255).endVertex(); + buffer.pos(-widthInner, -heightInner, 0).tex(0.2, 0.8).color(200, 200, 255, 255).endVertex(); + + buffer.pos(-widthOuter, heightInner, -depth).tex(0, 0.2).color(0, 0, 0, 255).endVertex(); + buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex(); + + tessellator.draw(); + + buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR); + + buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex(); + buffer.pos(widthInner, heightInner, 0).tex(0.8, 0.2).color(200, 200, 255, 255).endVertex(); + buffer.pos(-widthInner, -heightInner, 0).tex(0.2, 0.8).color(200, 200, 255, 255).endVertex(); + buffer.pos(widthInner, -heightInner, 0).tex(0.8, 0.8).color(200, 200, 255, 255).endVertex(); + + tessellator.draw(); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/model/ModelHammer.java b/src/main/java/electroblob/wizardry/client/model/ModelHammer.java new file mode 100644 index 00000000..e1445f05 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/ModelHammer.java @@ -0,0 +1,83 @@ +package electroblob.wizardry.client.model; + +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.entity.Entity; + +public class ModelHammer extends ModelBase +{ + ModelRenderer Shape1; + ModelRenderer Shape2; + ModelRenderer Shape3; + ModelRenderer Shape4; + ModelRenderer Shape5; + ModelRenderer Shape6; + + public ModelHammer() + { + textureWidth = 64; + textureHeight = 64; + + Shape1 = new ModelRenderer(this, 0, 0); + Shape1.addBox(0F, 0F, 0F, 20, 12, 12); + Shape1.setRotationPoint(-10F, 12F, -6F); + Shape1.setTextureSize(64, 64); + Shape1.mirror = true; + setRotation(Shape1, 0F, 0F, 0F); + Shape2 = new ModelRenderer(this, 0, 24); + Shape2.addBox(0F, 0F, 0F, 4, 14, 4); + Shape2.setRotationPoint(-2F, -2F, -2F); + Shape2.setTextureSize(64, 64); + Shape2.mirror = true; + setRotation(Shape2, 0F, 0F, 0F); + Shape3 = new ModelRenderer(this, 0, 49); + Shape3.addBox(0F, 0F, 0F, 5, 5, 5); + Shape3.setRotationPoint(-2.5F, -7F, -2.5F); + Shape3.setTextureSize(64, 64); + Shape3.mirror = true; + setRotation(Shape3, 0F, 0F, 0F); + Shape4 = new ModelRenderer(this, 0, 42); + Shape4.addBox(0F, 0F, 0F, 5, 2, 5); + Shape4.setRotationPoint(-2.5F, 10F, -2.5F); + Shape4.setTextureSize(64, 64); + Shape4.mirror = true; + setRotation(Shape4, 0F, 0F, 0F); + Shape5 = new ModelRenderer(this, 20, 24); + Shape5.addBox(0F, 0F, 0F, 2, 14, 14); + Shape5.setRotationPoint(-8F, 11F, -7F); + Shape5.setTextureSize(64, 64); + Shape5.mirror = true; + setRotation(Shape5, 0F, 0F, 0F); + Shape6 = new ModelRenderer(this, 20, 24); + Shape6.addBox(0F, 0F, 0F, 2, 14, 14); + Shape6.setRotationPoint(6F, 11F, -7F); + Shape6.setTextureSize(64, 64); + Shape6.mirror = true; + setRotation(Shape6, 0F, 0F, 0F); + } + + public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5) + { + super.render(entity, f, f1, f2, f3, f4, f5); + setRotationAngles(f, f1, f2, f3, f4, f5, entity); + Shape1.render(f5); + Shape2.render(f5); + Shape3.render(f5); + Shape4.render(f5); + Shape5.render(f5); + Shape6.render(f5); + } + + private void setRotation(ModelRenderer model, float x, float y, float z) + { + model.rotateAngleX = x; + model.rotateAngleY = y; + model.rotateAngleZ = z; + } + + public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5, Entity entity) + { + super.setRotationAngles(f, f1, f2, f3, f4, f5, entity); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/model/ModelIceGiant.java b/src/main/java/electroblob/wizardry/client/model/ModelIceGiant.java new file mode 100644 index 00000000..4a8591d7 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/ModelIceGiant.java @@ -0,0 +1,321 @@ +package electroblob.wizardry.client.model; + +import javax.vecmath.Matrix4f; +import javax.vecmath.Vector3f; + +import electroblob.wizardry.entity.living.EntityIceGiant; +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.math.MathHelper; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ModelIceGiant extends ModelBase +{ + /** The head model for the iron golem. */ + public ModelRenderer iceGiantHead; + + /** The body model for the iron golem. */ + public ModelRenderer iceGiantBody; + + /** The right arm model for the iron golem. */ + public ModelRenderer iceGiantRightArm; + + /** The left arm model for the iron golem. */ + public ModelRenderer iceGiantLeftArm; + + /** The left leg model for the Iron Golem. */ + public ModelRenderer iceGiantLeftLeg; + + /** The right leg model for the Iron Golem. */ + public ModelRenderer iceGiantRightLeg; + + ModelRenderer headSpike1; + ModelRenderer headSpike2; + ModelRenderer headSpike3; + ModelRenderer headSpike4; + ModelRenderer headSpike5; + ModelRenderer headSpike6; + ModelRenderer headSpike7; + ModelRenderer rightArmSpike1; + ModelRenderer rightArmSpike2; + ModelRenderer leftArmSpike1; + ModelRenderer leftArmSpike2; + ModelRenderer bodySpike1; + ModelRenderer bodySpike2; + ModelRenderer bodySpike3; + ModelRenderer bodySpike4; + ModelRenderer bodySpike5; + + public ModelIceGiant() + { + this(0.0F); + } + + public ModelIceGiant(float par1) + { + this(par1, -7.0F); + } + + public ModelIceGiant(float par1, float par2) + { + short short1 = 128; + short short2 = 128; + + this.iceGiantHead = (new ModelRenderer(this)).setTextureSize(short1, short2); + this.iceGiantHead.setRotationPoint(0.0F, 0.0F + par2, -1.0F); + this.iceGiantHead.setTextureOffset(0, 10).addBox(-6.0F, -14.0F, -6.5F, 12, 12, 12, par1); + + this.iceGiantBody = (new ModelRenderer(this)).setTextureSize(short1, short2); + this.iceGiantBody.setRotationPoint(0.0F, 0.0F + par2, 0.0F); + this.iceGiantBody.setTextureOffset(0, 40).addBox(-9.0F, -2.0F, -6.0F, 18, 12, 11, par1); + this.iceGiantBody.setTextureOffset(0, 70).addBox(-4.5F, 10.0F, -3.0F, 9, 5, 6, par1 + 0.5F); + + this.iceGiantRightArm = (new ModelRenderer(this)).setTextureSize(short1, short2); + this.iceGiantRightArm.setRotationPoint(0.0F, -7.0F, 0.0F); + this.iceGiantRightArm.setTextureOffset(60, 21).addBox(-13.0F, -2.5F, -3.0F, 4, 30, 6, par1); + + this.iceGiantLeftArm = (new ModelRenderer(this)).setTextureSize(short1, short2); + this.iceGiantLeftArm.setRotationPoint(0.0F, -7.0F, 0.0F); + this.iceGiantLeftArm.setTextureOffset(60, 58).addBox(9.0F, -2.5F, -3.0F, 4, 30, 6, par1); + + this.iceGiantLeftLeg = (new ModelRenderer(this, 0, 22)).setTextureSize(short1, short2); + this.iceGiantLeftLeg.setRotationPoint(-4.0F, 18.0F + par2, 0.0F); + this.iceGiantLeftLeg.setTextureOffset(37, 0).addBox(-3.5F, -3.0F, -3.0F, 6, 16, 5, par1); + + this.iceGiantRightLeg = (new ModelRenderer(this, 0, 22)).setTextureSize(short1, short2); + this.iceGiantRightLeg.mirror = true; + this.iceGiantRightLeg.setTextureOffset(60, 0).setRotationPoint(5.0F, 18.0F + par2, 0.0F); + this.iceGiantRightLeg.addBox(-3.5F, -3.0F, -3.0F, 6, 16, 5, par1); + + headSpike1 = new ModelRenderer(this, 0, 0).setTextureSize(short1, short2); + headSpike1.addBox(-4F, -4F, 0F, 4, 4, 4); + headSpike1.setRotationPoint(-4F, -10F, -5F); + headSpike1.mirror = true; + setRotationWithEulerYzx(headSpike1, -0.1047198F, -0.5235988F, 0.9599311F); + + headSpike2 = new ModelRenderer(this, 0, 0).setTextureSize(short1, short2); + headSpike2.addBox(0F, -4F, 0F, 4, 4, 4); + headSpike2.setRotationPoint(4F, -16F, 0F); + headSpike2.mirror = true; + setRotationWithEulerYzx(headSpike2, 0.5585054F, 0.9250245F, -0.5235988F); + + headSpike3 = new ModelRenderer(this, 0, 0).setTextureSize(short1, short2); + headSpike3.addBox(-2F, -2F, -2F, 4, 4, 4); + headSpike3.setRotationPoint(4F, -13F, 4F); + headSpike3.mirror = true; + setRotationWithEulerYzx(headSpike3, 0.7853982F, -1.396263F, 0.7853982F); + + headSpike4 = new ModelRenderer(this, 0, 0).setTextureSize(short1, short2); + headSpike4.addBox(-4F, -4F, 0F, 4, 4, 4); + headSpike4.setRotationPoint(-4F, -16F, 0F); + headSpike4.mirror = true; + setRotationWithEulerYzx(headSpike4, 0.5585054F, -0.9250245F, 0.5235988F); + + headSpike5 = new ModelRenderer(this, 0, 0).setTextureSize(short1, short2); + headSpike5.addBox(0F, -4F, 0F, 4, 4, 4); + headSpike5.setRotationPoint(4F, -10F, -5F); + headSpike5.mirror = true; + setRotationWithEulerYzx(headSpike5, 0.5235988F, -0.9599311F, 0.1047198F); + + rightArmSpike1 = new ModelRenderer(this, 0, 0).setTextureSize(short1, short2); + rightArmSpike1.addBox(-2F, -2F, -2F, 4, 4, 4); + rightArmSpike1.setRotationPoint(-11F, -8F, 0F); + rightArmSpike1.mirror = true; + setRotationWithEulerYzx(rightArmSpike1, 0.7853982F, 1.134464F, 0.9599311F); + + headSpike6 = new ModelRenderer(this, 0, 0).setTextureSize(short1, short2); + headSpike6.addBox(-2F, -2F, -2F, 4, 4, 4); + headSpike6.setRotationPoint(-4F, -13F, 4F); + headSpike6.mirror = true; + setRotationWithEulerYzx(headSpike6, 0.7853982F, -1.745329F, 0.7853982F); + + leftArmSpike1 = new ModelRenderer(this, 0, 0).setTextureSize(short1, short2); + leftArmSpike1.addBox(-2F, -2F, -2F, 4, 4, 4); + leftArmSpike1.setRotationPoint(11F, -8F, 0F); + leftArmSpike1.mirror = true; + setRotationWithEulerYzx(leftArmSpike1, 0.7853982F, -1.134464F, -0.9599311F); + + leftArmSpike2 = new ModelRenderer(this, 0, 0).setTextureSize(short1, short2); + leftArmSpike2.addBox(-2F, -2F, -2F, 4, 4, 4); + leftArmSpike2.setRotationPoint(12F, -4F, 0F); + leftArmSpike2.mirror = true; + setRotationWithEulerYzx(leftArmSpike2, 0.7853982F, 0F, -0.9599311F); + + bodySpike1 = new ModelRenderer(this, 32, 69).setTextureSize(short1, short2); + bodySpike1.addBox(-3F, -3F, -3F, 6, 6, 6); + bodySpike1.setRotationPoint(-4F, -4F, 3F); + bodySpike1.mirror = true; + setRotationWithEulerYzx(bodySpike1, 0.2808018F, 0.8096675F, 0.8339369F); + + rightArmSpike2 = new ModelRenderer(this, 0, 0).setTextureSize(short1, short2); + rightArmSpike2.addBox(-2F, -2F, -2F, 4, 4, 4); + rightArmSpike2.setRotationPoint(-12F, -4F, 0F); + rightArmSpike2.mirror = true; + setRotationWithEulerYzx(rightArmSpike2, 0.7853982F, 0F, 0.9599311F); + + bodySpike2 = new ModelRenderer(this, 32, 69).setTextureSize(short1, short2); + bodySpike2.addBox(-3F, -3F, -3F, 6, 6, 6); + bodySpike2.setRotationPoint(4F, -4F, 3F); + bodySpike2.mirror = true; + setRotationWithEulerYzx(bodySpike2, 0.2808018F, -0.8096757F, -0.8339358F); + + bodySpike3 = new ModelRenderer(this, 0, 0).setTextureSize(short1, short2); + bodySpike3.addBox(-2F, -2F, -2F, 4, 4, 4); + bodySpike3.setRotationPoint(6F, -2F, -5F); + bodySpike3.mirror = true; + setRotationWithEulerYzx(bodySpike3, 1.120006F, -1.347726F, -0.8969422F); + + bodySpike4 = new ModelRenderer(this, 32, 69).setTextureSize(short1, short2); + bodySpike4.addBox(-3F, -3F, -3F, 6, 6, 6); + bodySpike4.setRotationPoint(0F, -4F, -4F); + bodySpike4.mirror = true; + setRotationWithEulerYzx(bodySpike4, 0.7853982F, -1.570796F, 0.9599311F); + + headSpike7 = new ModelRenderer(this, 0, 0).setTextureSize(short1, short2); + headSpike7.addBox(-2F, -2F, -2F, 4, 4, 4); + headSpike7.setRotationPoint(0F, -20F, 4F); + headSpike7.mirror = true; + setRotationWithEulerYzx(headSpike7, 0.7853982F, 1.570796F, 0.7853982F); + + bodySpike5 = new ModelRenderer(this, 0, 0).setTextureSize(short1, short2); + bodySpike5.addBox(-2F, -2F, -2F, 4, 4, 4); + bodySpike5.setRotationPoint(-6F, -2F, -5F); + bodySpike5.mirror = true; + setRotationWithEulerYzx(bodySpike5, 1.120006F, 1.347725F, 0.896934F); + + this.convertToChild(this.iceGiantHead, headSpike1); + this.convertToChild(this.iceGiantHead, headSpike2); + this.convertToChild(this.iceGiantHead, headSpike3); + this.convertToChild(this.iceGiantHead, headSpike4); + this.convertToChild(this.iceGiantHead, headSpike5); + this.convertToChild(this.iceGiantHead, headSpike6); + this.convertToChild(this.iceGiantHead, headSpike7); + this.convertToChild(this.iceGiantRightArm, rightArmSpike1); + this.convertToChild(this.iceGiantRightArm, rightArmSpike2); + this.convertToChild(this.iceGiantLeftArm, leftArmSpike1); + this.convertToChild(this.iceGiantLeftArm, leftArmSpike2); + this.convertToChild(this.iceGiantBody, bodySpike1); + this.convertToChild(this.iceGiantBody, bodySpike2); + this.convertToChild(this.iceGiantBody, bodySpike3); + this.convertToChild(this.iceGiantBody, bodySpike4); + this.convertToChild(this.iceGiantBody, bodySpike5); + + } + + /** + * Sets the models various rotation angles then renders the model. + */ + public void render(Entity par1Entity, float par2, float par3, float par4, float par5, float par6, float par7) + { + this.setRotationAngles(par2, par3, par4, par5, par6, par7, par1Entity); + this.iceGiantHead.render(par7); + this.iceGiantBody.render(par7); + this.iceGiantLeftLeg.render(par7); + this.iceGiantRightLeg.render(par7); + this.iceGiantRightArm.render(par7); + this.iceGiantLeftArm.render(par7); + } + + /** + * Sets the model's various rotation angles. For bipeds, par1 and par2 are used for animating the movement of arms + * and legs, where par1 represents the time(so that arms and legs swing back and forth) and par2 represents how + * "far" arms and legs can swing at most. + */ + public void setRotationAngles(float par1, float par2, float par3, float par4, float par5, float par6, Entity par7Entity) + { + this.iceGiantHead.rotateAngleY = par4 / (180F / (float)Math.PI); + this.iceGiantHead.rotateAngleX = par5 / (180F / (float)Math.PI); + this.iceGiantLeftLeg.rotateAngleX = -1.5F * this.func_78172_a(par1, 13.0F) * par2; + this.iceGiantRightLeg.rotateAngleX = 1.5F * this.func_78172_a(par1, 13.0F) * par2; + this.iceGiantLeftLeg.rotateAngleY = 0.0F; + this.iceGiantRightLeg.rotateAngleY = 0.0F; + } + + /** + * Used for easily adding entity-dependent animations. The second and third float params here are the same second + * and third as in the setRotationAngles method. + */ + public void setLivingAnimations(EntityLivingBase par1EntityLivingBase, float par2, float par3, float par4) + { + EntityIceGiant entityicegiant = (EntityIceGiant)par1EntityLivingBase; + int i = entityicegiant.getAttackTimer(); + + if (i > 0) + { + this.iceGiantRightArm.rotateAngleX = -2.0F + 1.5F * this.func_78172_a((float)i - par4, 10.0F); + this.iceGiantLeftArm.rotateAngleX = -2.0F + 1.5F * this.func_78172_a((float)i - par4, 10.0F); + } + else + { + this.iceGiantRightArm.rotateAngleX = (-0.2F + 1.5F * this.func_78172_a(par2, 13.0F)) * par3; + this.iceGiantLeftArm.rotateAngleX = (-0.2F - 1.5F * this.func_78172_a(par2, 13.0F)) * par3; + } + } + + private float func_78172_a(float par1, float par2) + { + return (Math.abs(par1 % par2 - par2 * 0.5F) - par2 * 0.25F) / (par2 * 0.25F); + } + + /** This is really useful for converting the source from a Techne model export + * which will have absolute rotation points that need to be converted before + * creating the addChild() relationship. [Courtesy of jabelar] */ + protected void convertToChild(ModelRenderer parent, ModelRenderer child) + { + // move child rotation point to be relative to parent + child.rotationPointX -= parent.rotationPointX; + child.rotationPointY -= parent.rotationPointY; + child.rotationPointZ -= parent.rotationPointZ; + // make rotations relative to parent + child.rotateAngleX -= parent.rotateAngleX; + child.rotateAngleY -= parent.rotateAngleY; + child.rotateAngleZ -= parent.rotateAngleZ; + // create relationship + parent.addChild(child); + } + + /** Fixes the Techne rotation order bug. [Courtesy of tprk77] */ + private Vector3f ConvertEulerYzxToZyx(Vector3f eulerYzx) { + // Create a matrix from YZX ordered Euler angles + float a = MathHelper.cos(eulerYzx.x); + float b = MathHelper.sin(eulerYzx.x); + float c = MathHelper.cos(eulerYzx.y); + float d = MathHelper.sin(eulerYzx.y); + float e = MathHelper.cos(eulerYzx.z); + float f = MathHelper.sin(eulerYzx.z); + Matrix4f matrix = new Matrix4f(); + matrix.m00 = c * e; + matrix.m01 = b * d - a * c * f; + matrix.m02 = b * c * f + a * d; + matrix.m10 = f; + matrix.m11 = a * e; + matrix.m12 = -b * e; + matrix.m20 = -d * e; + matrix.m21 = a * d * f + b * c; + matrix.m22 = a * c - b * d * f; + matrix.m33 = 1.0F; + // Create ZYX ordered Euler angles from the matrix + Vector3f eulerZyx = new Vector3f(); + eulerZyx.y = (float) Math.asin(MathHelper.clamp_float(-matrix.m20, -1, 1)); + if (MathHelper.abs(matrix.m20) < 0.99999) { + eulerZyx.x = (float) Math.atan2(matrix.m21, matrix.m22); + eulerZyx.z = (float) Math.atan2(matrix.m10, matrix.m00); + } else { + eulerZyx.x = 0.0F; + eulerZyx.z = (float) Math.atan2(-matrix.m01, matrix.m11); + } + return eulerZyx; + } + + private void setRotationWithEulerYzx(ModelRenderer model, float x, float y, float z) { + Vector3f eulerYzx = new Vector3f(x, y, z); + Vector3f eulerZyx = ConvertEulerYzxToZyx(eulerYzx); + model.rotateAngleX = eulerZyx.x; + model.rotateAngleY = eulerZyx.y; + model.rotateAngleZ = eulerZyx.z; + } +} diff --git a/src/main/java/electroblob/wizardry/client/model/ModelPhoenix.java b/src/main/java/electroblob/wizardry/client/model/ModelPhoenix.java new file mode 100644 index 00000000..568b580c --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/ModelPhoenix.java @@ -0,0 +1,139 @@ +package electroblob.wizardry.client.model; + +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.MathHelper; + +public class ModelPhoenix extends ModelBase { + + ModelRenderer body; + ModelRenderer rightWing; + ModelRenderer leftWing; + ModelRenderer tailFeathers; + ModelRenderer tail; + ModelRenderer head; + ModelRenderer neck; + ModelRenderer beak; + ModelRenderer plume; + + public ModelPhoenix() + { + textureWidth = 64; + textureHeight = 64; + + /* For future reference: + * - setRotationPoint sets the origin of a part relative to that of its parent. + * - The first 3 arguments of addBox set the position of a part relative to its rotation point, and the last + * 3 arguments are the size of the part. + * (This means that rotation point and position seem to be the wrong way round, since changing the rotation point + * will move the component without changing which point on the component it rotates about.) + * - The two integer arguments in the ModelRenderer constructor are the texture offset. + * - Mirror does nothing unless you set it before addBox. + * - Rotation is the usual pitch, yaw, roll. + */ + + body = new ModelRenderer(this, 0, 34); + body.addBox(0F, 0F, -3F, 6, 15, 6); + body.setRotationPoint(-3F, 0F, -5F); + body.setTextureSize(64, 64); + body.mirror = true; + setRotation(body, 0.296706F, 0F, 0F); + + rightWing = new ModelRenderer(this, 0, 0); + rightWing.mirror = true; + rightWing.addBox(-27F, -27F, 0F, 27, 34, 0); + rightWing.setRotationPoint(0F, 5F, 0F); + rightWing.setTextureSize(64, 64); + setRotation(rightWing, 0.1745329F, 0F, 0F); + + leftWing = new ModelRenderer(this, 0, 0); + leftWing.addBox(0F, -27F, 0F, 27, 34, 0); + leftWing.setRotationPoint(6F, 5F, 0F); + leftWing.setTextureSize(64, 64); + setRotation(leftWing, 0.1745329F, 0F, 0F); + + tailFeathers = new ModelRenderer(this, 0, 57); + tailFeathers.addBox(-5F, 0F, 0F, 10, 7, 0); + tailFeathers.setRotationPoint(0F, 7F, 1F); + tailFeathers.setTextureSize(64, 64); + tailFeathers.mirror = true; + setRotation(tailFeathers, 0.5235988F, 0F, 0F); + + tail = new ModelRenderer(this, 20, 55); + tail.addBox(-1F, 0F, -1F, 2, 7, 2); + tail.setRotationPoint(3F, 15F, 2F); + tail.setTextureSize(64, 64); + tail.mirror = true; + setRotation(tail, 0.4014257F, 0F, 0F); + + head = new ModelRenderer(this, 24, 34); + head.addBox(-2F, -4F, -5F, 4, 4, 6); + head.setRotationPoint(0F, -4F, 0F); + head.setTextureSize(64, 64); + head.mirror = true; + setRotation(head, 0F, 0F, 0F); + + neck = new ModelRenderer(this, 24, 44); + neck.addBox(-1F, -4F, -1F, 2, 4, 2); + neck.setRotationPoint(0F, 0F, -4F); + neck.setTextureSize(64, 64); + neck.mirror = true; + setRotation(neck, 0.2443461F, 0F, 0F); + + beak = new ModelRenderer(this, 32, 44); + beak.addBox(-0.5F, 4F, -1F, 1, 2, 3); + beak.setRotationPoint(0F, -5F, -8F); + beak.setTextureSize(64, 64); + beak.mirror = true; + setRotation(beak, 0.2792527F, 0F, 0F); + + plume = new ModelRenderer(this, 28, 50); + plume.addBox(-0.03333334F, 1F, 0F, 0, 5, 5); + plume.setRotationPoint(0F, -7F, 0F); + plume.setTextureSize(64, 64); + plume.mirror = true; + setRotation(plume, 0F, 0F, 0F); + + neck.addChild(head); + head.addChild(plume); + head.addChild(beak); + tail.addChild(tailFeathers); + body.addChild(tail); + body.addChild(rightWing); + body.addChild(leftWing); + } + + public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5) + { + super.render(entity, f, f1, f2, f3, f4, f5); + setRotationAngles(f, f1, f2, f3, f4, f5, entity); + + //float f6 = (180F / (float)Math.PI); + this.neck.rotateAngleX = f4 / (180F / (float)Math.PI); + this.neck.rotateAngleY = f3 / (180F / (float)Math.PI); + this.neck.rotateAngleZ = 0.0F; + this.body.rotateAngleX = 0.3f + MathHelper.cos(f2 * 0.1F) * 0.15F; + this.body.rotateAngleY = 0.0F; + this.tail.rotateAngleX = this.body.rotateAngleX * 1.1f; + this.tailFeathers.rotateAngleX = this.body.rotateAngleX * 1.2f; + this.rightWing.rotateAngleY = MathHelper.cos(f2 * 0.3F) * (float)Math.PI * 0.15F; + this.leftWing.rotateAngleY = -this.rightWing.rotateAngleY; + + body.render(f5); + neck.render(f5); + } + + private void setRotation(ModelRenderer model, float x, float y, float z) + { + model.rotateAngleX = x; + model.rotateAngleY = y; + model.rotateAngleZ = z; + } + + public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5, Entity entity) + { + super.setRotationAngles(f, f1, f2, f3, f4, f5, entity); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/model/ModelWizard.java b/src/main/java/electroblob/wizardry/client/model/ModelWizard.java new file mode 100644 index 00000000..e10cf45e --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/ModelWizard.java @@ -0,0 +1,174 @@ +package electroblob.wizardry.client.model; + +import net.minecraft.client.model.ModelBiped; +import net.minecraft.client.model.ModelRenderer; + +public class ModelWizard extends ModelBiped +{ + //fields + ModelRenderer Shape5; + ModelRenderer Shape8; + ModelRenderer Shape9; + ModelRenderer Shape10; + ModelRenderer Shape7; + ModelRenderer Shape11; + ModelRenderer Shape12; + ModelRenderer beard; + ModelRenderer Shape13; + + public ModelWizard() + { + //super(0, 0, 64, 32); + /* + bipedRightLeg = new ModelRenderer(this, 32, 0); // 32 and 0 are the x and y texture offsets respectively. + bipedRightLeg.addBox(-2F, 0F, -2F, 4, 12, 4); // x, y, z, u, v, w. + bipedRightLeg.setRotationPoint(-2F, 12F, 0F); // Rotation point xyz (absolute, not relative) + bipedRightLeg.setTextureSize(64, 64); + bipedRightLeg.mirror = true; + setRotation(bipedRightLeg, 0F, 0F, 0F); + + bipedLeftLeg.mirror = true; + bipedLeftLeg = new ModelRenderer(this, 32, 0); + bipedLeftLeg.addBox(-2F, 0F, -2F, 4, 12, 4); + bipedLeftLeg.setRotationPoint(2F, 12F, 0F); + bipedLeftLeg.setTextureSize(64, 64); + bipedLeftLeg.mirror = true; + setRotation(bipedLeftLeg, 0F, 0F, 0F); + bipedLeftLeg.mirror = false; + + bipedBody = new ModelRenderer(this, 0, 16); + bipedBody.addBox(0F, 0F, 0F, 8, 12, 4); + bipedBody.setRotationPoint(-4F, 0F, -2F); + bipedBody.setTextureSize(64, 64); + bipedBody.mirror = true; + setRotation(bipedBody, 0F, 0F, 0F); + + bipedLeftArm.mirror = true; + bipedLeftArm = new ModelRenderer(this, 48, 0); + bipedLeftArm.addBox(-1F, 0F, -2F, 4, 12, 4); + bipedLeftArm.setRotationPoint(4F, 0F, 0F); + bipedLeftArm.setTextureSize(64, 64); + bipedLeftArm.mirror = true; + setRotation(bipedLeftArm, 0F, 0F, 0F); + bipedLeftArm.mirror = false; + + bipedRightArm = new ModelRenderer(this, 48, 0); + bipedRightArm.addBox(-3F, 0F, -2F, 4, 12, 4); + bipedRightArm.setRotationPoint(-4F, 0F, 0F); + bipedRightArm.setTextureSize(64, 64); + bipedRightArm.mirror = true; + setRotation(bipedRightArm, 0F, 0F, 0F); + + bipedHead = new ModelRenderer(this, 0, 0); + bipedHead.addBox(-4F, -8F, -4F, 8, 8, 8); + bipedHead.setRotationPoint(0F, 0F, 0F); + bipedHead.setTextureSize(64, 64); + bipedHead.mirror = true; + setRotation(bipedHead, 0F, 0F, 0F); + */ + Shape5 = new ModelRenderer(this, 0, 51); + Shape5.addBox(0F, 0F, 0F, 12, 1, 12); + Shape5.setRotationPoint(-6F, -7F, -6F); + Shape5.setTextureSize(64, 64); + Shape5.mirror = true; + setRotation(Shape5, 0F, 0F, 0F); + Shape8 = new ModelRenderer(this, 0, 32); + Shape8.addBox(0F, 0F, 0F, 6, 1, 6); + Shape8.setRotationPoint(-3F, -9F, -3F); + Shape8.setTextureSize(64, 64); + Shape8.mirror = true; + setRotation(Shape8, -0.0349066F, 0F, 0F); + Shape9 = new ModelRenderer(this, 24, 32); + Shape9.addBox(0F, 0F, 0F, 3, 3, 3); + Shape9.setRotationPoint(-1.5F, -13F, -0.5F); + Shape9.setTextureSize(64, 64); + Shape9.mirror = true; + setRotation(Shape9, -0.2511622F, 0F, 0F); + Shape10 = new ModelRenderer(this, 0, 39); + Shape10.addBox(0F, 0F, 0F, 5, 1, 5); + Shape10.setRotationPoint(-2.5F, -10F, -2.5F); + Shape10.setTextureSize(64, 64); + Shape10.mirror = true; + setRotation(Shape10, -0.0698132F, 0F, 0F); + Shape7 = new ModelRenderer(this, 0, 45); + Shape7.addBox(0F, 0F, 0F, 4, 2, 4); + Shape7.setRotationPoint(-2F, -11F, -1.5F); + Shape7.setTextureSize(64, 64); + Shape7.mirror = true; + setRotation(Shape7, -0.1396263F, 0F, 0F); + Shape11 = new ModelRenderer(this, 20, 39); + Shape11.addBox(0F, 0F, 0F, 2, 3, 2); + Shape11.setRotationPoint(-1F, -15F, 1F); + Shape11.setTextureSize(64, 64); + Shape11.mirror = true; + setRotation(Shape11, -0.4363323F, 0F, 0F); + Shape12 = new ModelRenderer(this, 28, 39); + Shape12.addBox(0F, 0F, 0F, 1, 2, 1); + Shape12.setRotationPoint(-0.5F, -16F, 2.5F); + Shape12.setTextureSize(64, 64); + Shape12.mirror = true; + setRotation(Shape12, -0.715585F, 0F, 0F); + beard = new ModelRenderer(this, 32, 0); + beard.addBox(0F, 0F, 0F, 8, 5, 0); + beard.setRotationPoint(-4F, 0F, -4F); + beard.setTextureSize(64, 64); + beard.mirror = true; + setRotation(beard, 0F, 0F, 0F); + Shape13 = new ModelRenderer(this, 36, 16); + Shape13.addBox(4F, 0F, 2F, 8, 20, 6); + Shape13.setRotationPoint(-4F, 0F, -3F); + Shape13.setTextureSize(64, 64); + Shape13.mirror = true; + setRotation(Shape13, 0F, 0F, 0F); + + // Makes head bits move with head + //bipedHead.addChild(Shape5); + bipedHead.addChild(beard); + //bipedHead.addChild(Shape7); + //bipedHead.addChild(Shape8); + //bipedHead.addChild(Shape9); + //bipedHead.addChild(Shape10); + //bipedHead.addChild(Shape11); + //bipedHead.addChild(Shape12); + + // Makes cloak attached to body + //bipedBody.addChild(Shape13); + + // No outer head layer + this.bipedHeadwear.isHidden = true; + } + /* + public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5) + { + super.render(entity, f, f1, f2, f3, f4, f5); + setRotationAngles(f, f1, f2, f3, f4, f5, entity); + bipedRightLeg.render(f5); + bipedLeftLeg.render(f5); + bipedBody.render(f5); + bipedLeftArm.render(f5); + bipedRightArm.render(f5); + bipedHead.render(f5); + Shape5.render(f5); + Shape8.render(f5); + Shape9.render(f5); + Shape10.render(f5); + Shape7.render(f5); + Shape11.render(f5); + Shape12.render(f5); + Shape6.render(f5); + Shape13.render(f5); + } + */ + private void setRotation(ModelRenderer model, float x, float y, float z) + { + model.rotateAngleX = x; + model.rotateAngleY = y; + model.rotateAngleZ = z; + } + /* + public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5, Entity entity) + { + super.setRotationAngles(f, f1, f2, f3, f4, f5, entity); + } +*/ +} diff --git a/src/main/java/electroblob/wizardry/client/model/ModelWizardArmour.java b/src/main/java/electroblob/wizardry/client/model/ModelWizardArmour.java new file mode 100644 index 00000000..3829f103 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/ModelWizardArmour.java @@ -0,0 +1,111 @@ +package electroblob.wizardry.client.model; + +import net.minecraft.client.model.ModelBiped; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.entity.Entity; + +public class ModelWizardArmour extends ModelBiped +{ + ModelRenderer Shape1; + ModelRenderer Shape2; + ModelRenderer Shape3; + ModelRenderer Shape4; + ModelRenderer Shape5; + ModelRenderer Shape6; + ModelRenderer Shape7; + ModelRenderer robe; + + public ModelWizardArmour(float scale){ + + super(scale, 0, 64, 64); + + // This is necessary to stop the head from scaling. + this.bipedHead = new ModelRenderer(this, 0, 0); + this.bipedHead.addBox(-4.0F, -8.0F, -4.0F, 8, 8, 8, 0.1f); + this.bipedHead.setRotationPoint(0.0F, 0.0F + 0, 0.0F); + + Shape1 = new ModelRenderer(this, -16, 32); + Shape1.addBox(-8F, -7F, -8F, 16, 0, 16); + Shape1.setRotationPoint(0F, 0F, 0F); + Shape1.setTextureSize(64, 64); + Shape1.mirror = true; + setRotation(Shape1, 0F, 0F, 0F); + + Shape2 = new ModelRenderer(this, 0, 48); + Shape2.addBox(0F, 0F, 0F, 6, 2, 6); + Shape2.setRotationPoint(-3F, -10F, -3F); + Shape2.setTextureSize(64, 64); + Shape2.mirror = true; + setRotation(Shape2, -0.1396263F, 0F, 0F); + + Shape3 = new ModelRenderer(this, 0, 56); + Shape3.addBox(0F, 0F, 0F, 5, 2, 5); + Shape3.setRotationPoint(-2.5F, -11.53333F, -2F); + Shape3.setTextureSize(64, 64); + Shape3.mirror = true; + setRotation(Shape3, -0.2443461F, 0F, 0F); + + Shape4 = new ModelRenderer(this, 24, 48); + Shape4.addBox(0F, 0F, 0F, 4, 2, 4); + Shape4.setRotationPoint(-2F, -13F, -1F); + Shape4.setTextureSize(64, 64); + Shape4.mirror = true; + setRotation(Shape4, -0.4014257F, 0F, 0F); + + Shape5 = new ModelRenderer(this, 24, 54); + Shape5.addBox(0F, 0F, 0F, 3, 2, 3); + Shape5.setRotationPoint(-1.5F, -14F, 0F); + Shape5.setTextureSize(64, 64); + Shape5.mirror = true; + setRotation(Shape5, -0.5759587F, 0F, 0F); + + Shape6 = new ModelRenderer(this, 20, 59); + Shape6.addBox(0F, 0F, 0F, 2, 2, 2); + Shape6.setRotationPoint(-1F, -14F, 0F); + Shape6.setTextureSize(64, 64); + Shape6.mirror = true; + setRotation(Shape6, 0.3316126F, 0F, 0F); + + Shape7 = new ModelRenderer(this, 28, 59); + Shape7.addBox(0F, 0F, 0F, 1, 1, 3); + Shape7.setRotationPoint(-0.5F, -14.5F, 2F); + Shape7.setTextureSize(64, 64); + Shape7.mirror = true; + setRotation(Shape7, -0.5585054F, 0F, 0F); + + // The robe is now the body + bipedBody = new ModelRenderer(this, 40, 42); + bipedBody.addBox(-4F, 0F, -2F, 8, 18, 4, scale); + bipedBody.setRotationPoint(0F, 0F, 0F); + bipedBody.setTextureSize(64, 64); + bipedBody.mirror = true; + setRotation(bipedBody, 0F, 0F, 0F); + + // Makes the hat rotate with the head. + bipedHead.addChild(Shape1); + bipedHead.addChild(Shape2); + bipedHead.addChild(Shape3); + bipedHead.addChild(Shape4); + bipedHead.addChild(Shape5); + bipedHead.addChild(Shape6); + bipedHead.addChild(Shape7); + // Makes the robe move with the body + //bipedBody.addChild(robe); + } + + public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5){ + super.render(entity, f, f1, f2, f3, f4, f5); + setRotationAngles(f, f1, f2, f3, f4, f5, entity); + } + + private void setRotation(ModelRenderer model, float x, float y, float z){ + model.rotateAngleX = x; + model.rotateAngleY = y; + model.rotateAngleZ = z; + } + + public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5, Entity entity){ + super.setRotationAngles(f, f1, f2, f3, f4, f5, entity); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/model/WizardryItemModels.java b/src/main/java/electroblob/wizardry/client/model/WizardryItemModels.java new file mode 100644 index 00000000..a1d678ab --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/WizardryItemModels.java @@ -0,0 +1,192 @@ +package electroblob.wizardry.client.model; + +import java.util.ArrayList; +import java.util.List; + +import electroblob.wizardry.registry.WizardryBlocks; +import electroblob.wizardry.registry.WizardryItems; +import net.minecraft.client.renderer.block.model.ModelResourceLocation; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.client.event.ModelRegistryEvent; +import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.oredict.OreDictionary; + +/** + * Class responsible for registering all of wizardry's item (and itemblock) models. + * @author Electroblob + * @since Wizardry 2.1 + */ +@SideOnly(Side.CLIENT) +@Mod.EventBusSubscriber(Side.CLIENT) +public final class WizardryItemModels { + + @SubscribeEvent + public static void register(ModelRegistryEvent event){ + + // ItemBlocks + + registerItemModel(Item.getItemFromBlock(WizardryBlocks.arcane_workbench)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_ore)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_flower)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.transportation_stone)); + registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_block)); + + // Items + + registerItemModel(WizardryItems.magic_crystal); + + registerItemModel(WizardryItems.magic_wand); + registerItemModel(WizardryItems.apprentice_wand); + registerItemModel(WizardryItems.advanced_wand); + registerItemModel(WizardryItems.master_wand); + + registerItemModel(WizardryItems.spell_book); + // Wildcard registered for wizard trades. + registerItemModel(WizardryItems.spell_book, OreDictionary.WILDCARD_VALUE, "normal"); + registerItemModel(WizardryItems.arcane_tome); + registerItemModel(WizardryItems.wizard_handbook); + + registerItemModel(WizardryItems.basic_fire_wand); + registerItemModel(WizardryItems.basic_ice_wand); + registerItemModel(WizardryItems.basic_lightning_wand); + registerItemModel(WizardryItems.basic_necromancy_wand); + registerItemModel(WizardryItems.basic_earth_wand); + registerItemModel(WizardryItems.basic_sorcery_wand); + registerItemModel(WizardryItems.basic_healing_wand); + + registerItemModel(WizardryItems.apprentice_fire_wand); + registerItemModel(WizardryItems.apprentice_ice_wand); + registerItemModel(WizardryItems.apprentice_lightning_wand); + registerItemModel(WizardryItems.apprentice_necromancy_wand); + registerItemModel(WizardryItems.apprentice_earth_wand); + registerItemModel(WizardryItems.apprentice_sorcery_wand); + registerItemModel(WizardryItems.apprentice_healing_wand); + + registerItemModel(WizardryItems.advanced_fire_wand); + registerItemModel(WizardryItems.advanced_ice_wand); + registerItemModel(WizardryItems.advanced_lightning_wand); + registerItemModel(WizardryItems.advanced_necromancy_wand); + registerItemModel(WizardryItems.advanced_earth_wand); + registerItemModel(WizardryItems.advanced_sorcery_wand); + registerItemModel(WizardryItems.advanced_healing_wand); + + registerItemModel(WizardryItems.master_fire_wand); + registerItemModel(WizardryItems.master_ice_wand); + registerItemModel(WizardryItems.master_lightning_wand); + registerItemModel(WizardryItems.master_necromancy_wand); + registerItemModel(WizardryItems.master_earth_wand); + registerItemModel(WizardryItems.master_sorcery_wand); + registerItemModel(WizardryItems.master_healing_wand); + + registerItemModel(WizardryItems.spectral_sword); + registerItemModel(WizardryItems.spectral_pickaxe); + registerItemModel(WizardryItems.spectral_bow); + + registerItemModel(WizardryItems.mana_flask); + + registerItemModel(WizardryItems.storage_upgrade); + registerItemModel(WizardryItems.siphon_upgrade); + registerItemModel(WizardryItems.condenser_upgrade); + registerItemModel(WizardryItems.range_upgrade); + registerItemModel(WizardryItems.duration_upgrade); + registerItemModel(WizardryItems.cooldown_upgrade); + registerItemModel(WizardryItems.blast_upgrade); + registerItemModel(WizardryItems.attunement_upgrade); + + registerItemModel(WizardryItems.flaming_axe); + registerItemModel(WizardryItems.frost_axe); + + registerItemModel(WizardryItems.firebomb); + registerItemModel(WizardryItems.poison_bomb); + + registerItemModel(WizardryItems.blank_scroll); + registerItemModel(WizardryItems.scroll); + + registerItemModel(WizardryItems.armour_upgrade); + + registerItemModel(WizardryItems.magic_silk); + + registerItemModel(WizardryItems.wizard_hat); + registerItemModel(WizardryItems.wizard_robe); + registerItemModel(WizardryItems.wizard_leggings); + registerItemModel(WizardryItems.wizard_boots); + + registerItemModel(WizardryItems.wizard_hat_fire); + registerItemModel(WizardryItems.wizard_robe_fire); + registerItemModel(WizardryItems.wizard_leggings_fire); + registerItemModel(WizardryItems.wizard_boots_fire); + + registerItemModel(WizardryItems.wizard_hat_ice); + registerItemModel(WizardryItems.wizard_robe_ice); + registerItemModel(WizardryItems.wizard_leggings_ice); + registerItemModel(WizardryItems.wizard_boots_ice); + + registerItemModel(WizardryItems.wizard_hat_lightning); + registerItemModel(WizardryItems.wizard_robe_lightning); + registerItemModel(WizardryItems.wizard_leggings_lightning); + registerItemModel(WizardryItems.wizard_boots_lightning); + + registerItemModel(WizardryItems.wizard_hat_necromancy); + registerItemModel(WizardryItems.wizard_robe_necromancy); + registerItemModel(WizardryItems.wizard_leggings_necromancy); + registerItemModel(WizardryItems.wizard_boots_necromancy); + + registerItemModel(WizardryItems.wizard_hat_earth); + registerItemModel(WizardryItems.wizard_robe_earth); + registerItemModel(WizardryItems.wizard_leggings_earth); + registerItemModel(WizardryItems.wizard_boots_earth); + + registerItemModel(WizardryItems.wizard_hat_sorcery); + registerItemModel(WizardryItems.wizard_robe_sorcery); + registerItemModel(WizardryItems.wizard_leggings_sorcery); + registerItemModel(WizardryItems.wizard_boots_sorcery); + + registerItemModel(WizardryItems.wizard_hat_healing); + registerItemModel(WizardryItems.wizard_robe_healing); + registerItemModel(WizardryItems.wizard_leggings_healing); + registerItemModel(WizardryItems.wizard_boots_healing); + + registerItemModel(WizardryItems.spectral_helmet); + registerItemModel(WizardryItems.spectral_chestplate); + registerItemModel(WizardryItems.spectral_leggings); + registerItemModel(WizardryItems.spectral_boots); + + registerItemModel(WizardryItems.smoke_bomb); + + registerItemModel(WizardryItems.identification_scroll); + } + + // Moved from the proxies + + /** Registers an item model, using the item's registry name as the model name (this + * convention makes it easier to keep track of everything). Variant defaults to "normal". Registers the model + * for metadata 0 automatically, plus all the other metadata values that the item can take, as defined in + * {@link Item#getSubItems(Item, net.minecraft.creativetab.CreativeTabs, java.util.List)}. The passed in item + * must allow null to be passed in for the creative tab parameter in the aforementioned method, or a + * {@link NullPointerException} will result. */ + private static void registerItemModel(Item item){ + + if(item.getHasSubtypes()){ + List items = new ArrayList(); + item.getSubItems(item, null, items); // Client-only method, but we're client-side so this is OK. + for(ItemStack stack : items){ + ModelLoader.setCustomModelResourceLocation(item, stack.getMetadata(), new ModelResourceLocation(item.getRegistryName(), "inventory")); + } + } + // Changing the last parameter from null to "inventory" fixed the item/block model weirdness. No idea why! + ModelLoader.setCustomModelResourceLocation(item, 0, new ModelResourceLocation(item.getRegistryName(), "inventory")); + } + + /** Registers an item model for the given metadata, using the item's registry name as the model name (this + * convention makes it easier to keep track of everything). This is intended for registering additional metadata + * values which aren't displayed in the creative menu, for example the wildcard spell book used in wizard trades. */ + private static void registerItemModel(Item item, int metadata, String variant) { + ModelLoader.setCustomModelResourceLocation(item, metadata, new ModelResourceLocation(item.getRegistryName(), variant)); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleBlizzard.java b/src/main/java/electroblob/wizardry/client/particle/ParticleBlizzard.java new file mode 100644 index 00000000..4ea3114b --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleBlizzard.java @@ -0,0 +1,72 @@ +package electroblob.wizardry.client.particle; + +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ParticleBlizzard extends ParticleSnow { + + private double angle; + private double radius; + private double speed; + + public ParticleBlizzard(World world, int maxAge, double originX, double originZ, double radius, double yPos){ + super(world, 0, 0, 0, 0, 0, 0, maxAge); + this.angle = this.rand.nextDouble() * Math.PI * 2; + double x = originX - Math.cos(angle)*radius; + double z = originZ + radius*Math.sin(angle); + this.radius = radius; + this.setPosition(x, yPos, z); + this.prevPosX = x; + this.prevPosY = yPos; + this.prevPosZ = z; + if(rand.nextBoolean()){ + speed = rand.nextDouble()*2 + 1; + }else{ + speed = rand.nextDouble()*-2 - 1; + } + this.multipleParticleScaleBy(1.5f); + } + + @Override + public void init(){ + super.init(); + this.fullBrightness = true; + } + +// @Override +// public void renderParticle(VertexBuffer buffer, Entity entity, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ){ +// if(this.particleAge < this.particleMaxAge / 3 || (this.particleAge + this.particleMaxAge) / 3 % 2 == 0){ +// super.renderParticle(buffer, entity, partialTicks, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ); +// } +// } + + @Override + public void onUpdate(){ + + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + + if(this.particleAge++ >= this.particleMaxAge){ + this.setExpired(); + } + + // This is in radians per tick... + double omega = Math.signum(speed) * ((Math.PI*2)/20 - speed/(20*radius)); + + // v = r times omega; therefore the normalised velocity vector needs to be r times the angle increment / 2 pi. + this.angle += omega; + + this.motionY -= 0.04D * (double)this.particleGravity; + this.motionZ = radius * omega * Math.cos(angle); + this.motionX = radius * omega * Math.sin(angle); + this.moveEntity(motionX, motionY, motionZ); + + if(this.particleAge > this.particleMaxAge / 2){ + this.setAlphaF(1.0F - ((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge); + } + + } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleCustomTexture.java b/src/main/java/electroblob/wizardry/client/particle/ParticleCustomTexture.java new file mode 100644 index 00000000..3d5fa986 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleCustomTexture.java @@ -0,0 +1,195 @@ +package electroblob.wizardry.client.particle; + +import java.util.List; + +import org.lwjgl.opengl.GL11; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.Particle; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.entity.Entity; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** + * Abstract superclass for all particles that use custom textures. This is intended to centralise as much code as + * possible; all subclasses need to do is to define the texture to use, how the frames are arranged (and which to + * choose), and any properties like gravity and collisions. + * @author Electroblob + * @since Wizardry 1.2 + */ +@SideOnly(Side.CLIENT) +public abstract class ParticleCustomTexture extends Particle { + + /** True if the particle always renders at full brightness. Defaults to false. */ + protected boolean fullBrightness = false; + + public ParticleCustomTexture(World world, double x, double y, double z, double vx, double vy, double vz){ + super(world, x, y, z, vx, vy, vz); + this.motionX = vx; + this.motionY = vy; + this.motionZ = vz; + this.init(); + } + + public ParticleCustomTexture(World world, double x, double y, double z, double vx, double vy, double vz, int maxAge){ + this(world, x, y, z, vx, vy, vz); + this.particleMaxAge = maxAge; + } + + /** Called from both constructors to set constants, avoiding duplicate code. Common fields to set here + * include: particleScale, particleGravity, canCollide, fullBrightness and setting the texture index. */ + public abstract void init(); + /** Returns a ResourceLocation for the particle's texture sheet. Do not create a new ResourceLocation in this + * method, only return a constant. */ + public abstract ResourceLocation getTexture(); + /** Returns how many 'frames' there are in the x direction on the texture. */ + protected abstract int getXFrames(); + /** Returns how many 'frames' there are in the y direction on the texture. */ + protected abstract int getYFrames(); + + /* There are 4 layers of particles, specified as 0-3 by the method below. + * - Layer 0 causes the normal particles.png to be bound to the render engine for normal particles. + * - Layer 1 causes the block textures to be bound to the render engine for digging fx and falling fx. + * - Layer 2 causes the item textures to be bound to the render engine for tool breaking fx, snowballpoofs, slime particles, etc. + * - Layer 3 is not used in vanilla minecraft and was presumably added by forge for exactly this reason. + * This means no texture is bound by vanilla minecraft, meaning you are free to do as you wish without possibly + * overwriting vanilla particles. Mod particles won't be overwritten anyway since they bind their own textures. + * It is of course important to bind the texture every time you render a custom particle, but I don't see how + * you could do it any other way, since you don't have access to EffectRenderer. */ + @Override + public int getFXLayer() { + // This can only be 0-3 or it will cause an ArrayIndexOutOfBoundsException in EffectRenderer. + return 3; + } + + @Override + public void setParticleTextureIndex(int index){ + this.particleTextureIndexX = index % getXFrames(); + this.particleTextureIndexY = index / getYFrames(); + } + + // Overridden to fix the bug with vanilla that makes particles frictionless. (y != y... seriously, Mojang?) + @Override + public void moveEntity(double x, double y, double z){ + + double d0 = y; + + if (this.canCollide) + { + List list = this.worldObj.getCollisionBoxes((Entity)null, this.getEntityBoundingBox().addCoord(x, y, z)); + + for (AxisAlignedBB axisalignedbb : list) + { + y = axisalignedbb.calculateYOffset(this.getEntityBoundingBox(), y); + } + + this.setEntityBoundingBox(this.getEntityBoundingBox().offset(0.0D, y, 0.0D)); + + for (AxisAlignedBB axisalignedbb1 : list) + { + x = axisalignedbb1.calculateXOffset(this.getEntityBoundingBox(), x); + } + + this.setEntityBoundingBox(this.getEntityBoundingBox().offset(x, 0.0D, 0.0D)); + + for (AxisAlignedBB axisalignedbb2 : list) + { + z = axisalignedbb2.calculateZOffset(this.getEntityBoundingBox(), z); + } + + this.setEntityBoundingBox(this.getEntityBoundingBox().offset(0.0D, 0.0D, z)); + } + else + { + this.setEntityBoundingBox(this.getEntityBoundingBox().offset(x, y, z)); + } + + this.resetPositionToBB(); + this.isCollided = d0 != y && d0 < 0.0D; + + /* Can never be true! - But this doesn't seem to make any difference anyway. + if (x != x) + { + this.motionX = 0.0D; + } + + if (z != z) + { + this.motionZ = 0.0D; + } + */ + } + + // Overridden to bind the new texture. I think this can be done with TextureAtlasSprite, but this works as it is + // so I'm not changing it for the time being. + @Override + public void renderParticle(VertexBuffer buffer, Entity viewer, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ){ + + GlStateManager.pushMatrix(); + GlStateManager.pushAttrib(); + + this.applyGLStateChanges(); + + // This stuff does the shading. It vanilla does this later on for each point, but this also seems to work. + int brightness = this.getBrightnessForRender(partialTicks); + int lightmapX = brightness % 65536; + int lightmapY = brightness / 65536; + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)lightmapX / 1.0F, (float)lightmapY / 1.0F); + + RenderHelper.disableStandardItemLighting(); + + Minecraft.getMinecraft().getTextureManager().bindTexture(getTexture()); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR); + + float u1 = (float)this.particleTextureIndexX / (float)getXFrames(); + float u2 = u1 + 1.0f/getXFrames(); + float v1 = (float)this.particleTextureIndexY / (float)getYFrames(); + float v2 = v1 + 1.0f/getYFrames(); + float scale = 0.1F * this.particleScale; + + // I'm pretty sure these were always static. + Particle.interpPosX = viewer.lastTickPosX + (viewer.posX - viewer.lastTickPosX) * (double)partialTicks; + Particle.interpPosY = viewer.lastTickPosY + (viewer.posY - viewer.lastTickPosY) * (double)partialTicks; + Particle.interpPosZ = viewer.lastTickPosZ + (viewer.posZ - viewer.lastTickPosZ) * (double)partialTicks; + + float x = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX); + float y = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY); + float z = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ); + + buffer.pos((double)(x - rotationX * scale - rotationXY * scale), (double)(y - rotationZ * scale), (double)(z - rotationYZ * scale - rotationXZ * scale)).tex(u2, v2).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex(); + buffer.pos((double)(x - rotationX * scale + rotationXY * scale), (double)(y + rotationZ * scale), (double)(z - rotationYZ * scale + rotationXZ * scale)).tex(u2, v1).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex(); + buffer.pos((double)(x + rotationX * scale + rotationXY * scale), (double)(y + rotationZ * scale), (double)(z + rotationYZ * scale + rotationXZ * scale)).tex(u1, v1).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex(); + buffer.pos((double)(x + rotationX * scale - rotationXY * scale), (double)(y - rotationZ * scale), (double)(z + rotationYZ * scale - rotationXZ * scale)).tex(u1, v2).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();; + + Tessellator.getInstance().draw(); + + this.undoGLStateChanges(); + + GlStateManager.popAttrib(); + GlStateManager.popMatrix(); + + } + + /** Override to add any GL state changes, like blending. Does nothing by default. State changes should be + * done using GLStateManager, not using GL11 directly (as is the case with all rendering code now). */ + public void applyGLStateChanges(){} + + /** Override to undo any GL state changes, like blending. Does nothing by default. State changes should be + * done using GLStateManager, not using GL11 directly (as is the case with all rendering code now). */ + public void undoGLStateChanges(){} + + @Override + public int getBrightnessForRender(float partialTick){ + return fullBrightness ? 15728880 : super.getBrightnessForRender(partialTick); + } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleDarkMagic.java b/src/main/java/electroblob/wizardry/client/particle/ParticleDarkMagic.java new file mode 100644 index 00000000..986e3c7c --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleDarkMagic.java @@ -0,0 +1,90 @@ +package electroblob.wizardry.client.particle; + +import net.minecraft.client.particle.Particle; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.entity.Entity; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ParticleDarkMagic extends Particle { + + /** Base spell texture index */ + private int baseSpellTextureIndex = 128; + + public ParticleDarkMagic(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, float r, float g, float b) + { + super(par1World, par2, par4, par6, par8, par10, par12); + this.motionY *= 0.20000000298023224D; + + this.particleRed = r; + this.particleGreen = g; + this.particleBlue = b; + + this.particleScale *= 0.75F; + this.particleMaxAge = (int)(8.0D / (Math.random() * 0.8D + 0.2D)); + this.canCollide = true; + } + + @Override + public void renderParticle(VertexBuffer buffer, Entity entity, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ) + { + float f6 = ((float)this.particleAge + partialTicks) / (float)this.particleMaxAge * 32.0F; + + if (f6 < 0.0F) + { + f6 = 0.0F; + } + + if (f6 > 1.0F) + { + f6 = 1.0F; + } + + super.renderParticle(buffer, entity, partialTicks, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ); + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate() + { + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + + if (this.particleAge++ >= this.particleMaxAge) + { + this.setExpired(); + } + + this.setParticleTextureIndex(this.baseSpellTextureIndex + (7 - this.particleAge * 8 / this.particleMaxAge)); + this.motionY += 0.004D; + this.moveEntity(this.motionX, this.motionY, this.motionZ); + /* + if (this.posY == this.prevPosY) + { + this.motionX *= 1.1D; + this.motionZ *= 1.1D; + } + */ + this.motionX *= 0.9599999785423279D; + this.motionY *= 0.9599999785423279D; + this.motionZ *= 0.9599999785423279D; + + if (this.isCollided) + { + this.motionX *= 0.699999988079071D; + this.motionZ *= 0.699999988079071D; + } + } + + /** + * Sets the base spell texture index + */ + public void setBaseSpellTextureIndex(int par1) + { + this.baseSpellTextureIndex = par1; + } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleDust.java b/src/main/java/electroblob/wizardry/client/particle/ParticleDust.java new file mode 100644 index 00000000..6a121fa3 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleDust.java @@ -0,0 +1,57 @@ +package electroblob.wizardry.client.particle; + +import net.minecraft.client.particle.Particle; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ParticleDust extends Particle { + + private final boolean shaded; + + public ParticleDust(World par1World, double x, double y, double z, double par8, double par10, double par12, float r, float g, float b, boolean shaded) + { + super(par1World, x, y, z, par8, par10, par12); + this.particleRed = r; + this.particleGreen = g; + this.particleBlue = b; + this.setParticleTextureIndex(0); + this.setSize(0.01F, 0.01F); + this.particleScale *= this.rand.nextFloat() + 0.2F; + this.motionX = par8; + this.motionY = par10; + this.motionZ = par12; + this.particleMaxAge = (int)(16.0D / (Math.random() * 0.8D + 0.2D)); + this.shaded = shaded; + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate() + { + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + //this.moveEntity(this.motionX, this.motionY, this.motionZ); + + if (this.particleMaxAge-- <= 0) + { + this.setExpired(); + } + } + + @Override + public int getBrightnessForRender(float par1) + { + return shaded ? super.getBrightnessForRender(par1) : 15728880; + } + /* + @Override + public float getBrightness(float par1) + { + return shaded ? super.getBrightness(par1) : 1.0F; + } + */ +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleGiantBubble.java b/src/main/java/electroblob/wizardry/client/particle/ParticleGiantBubble.java new file mode 100644 index 00000000..b469bcb2 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleGiantBubble.java @@ -0,0 +1,50 @@ +package electroblob.wizardry.client.particle; + +import electroblob.wizardry.Wizardry; +import net.minecraft.client.particle.Particle; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ParticleGiantBubble extends Particle +{ + /** The name used to identify this particle. Uses the mod id to avoid any possible conflicts (Not that there would + * be any, but I may as well.) */ + public static final String NAME = Wizardry.MODID + "magicbubble"; + + public ParticleGiantBubble(World par1World, double par2, double par4, double par6, double par8, double par10, double par12) + { + super(par1World, par2, par4, par6, par8, par10, par12); + this.particleRed = 1.0F; + this.particleGreen = 1.0F; + this.particleBlue = 1.0F; + this.setParticleTextureIndex(32); + this.setSize(0.02F, 0.02F); + this.particleScale *= this.rand.nextFloat() * 0.6F + 0.2F; + this.motionX = par8 * 0.20000000298023224D + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.02F); + this.motionY = par10 * 0.20000000298023224D + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.02F); + this.motionZ = par12 * 0.20000000298023224D + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.02F); + this.particleMaxAge = (int)(8.0D / (Math.random() * 0.8D + 0.2D)); + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate() + { + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + this.motionY += 0.002D; + this.moveEntity(this.motionX, this.motionY, this.motionZ); + this.motionX *= 0.8500000238418579D; + this.motionY *= 0.8500000238418579D; + this.motionZ *= 0.8500000238418579D; + + if (this.particleMaxAge-- <= 0) + { + this.setExpired(); + } + } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleIce.java b/src/main/java/electroblob/wizardry/client/particle/ParticleIce.java new file mode 100644 index 00000000..3332db15 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleIce.java @@ -0,0 +1,34 @@ +package electroblob.wizardry.client.particle; + +import electroblob.wizardry.Wizardry; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ParticleIce extends ParticleCustomTexture { + + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/ice_particles.png"); + + public ParticleIce(World world, double x, double y, double z, double vx, double vy, double vz){ + super(world, x, y, z, vx, vy, vz); + } + + public ParticleIce(World world, double x, double y, double z, double vx, double vy, double vz, int maxAge){ + super(world, x, y, z, vx, vy, vz, maxAge); + } + + @Override + public void init(){ + this.setParticleTextureIndex(rand.nextInt(8)); + this.particleScale *= 0.75f; + this.particleGravity = 1; + this.canCollide = true; + this.fullBrightness = true; + } + + @Override public ResourceLocation getTexture(){ return TEXTURE; } + @Override protected int getXFrames(){ return 4; } + @Override protected int getYFrames(){ return 4; } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleLeaf.java b/src/main/java/electroblob/wizardry/client/particle/ParticleLeaf.java new file mode 100644 index 00000000..0292dddf --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleLeaf.java @@ -0,0 +1,33 @@ +package electroblob.wizardry.client.particle; + +import electroblob.wizardry.Wizardry; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ParticleLeaf extends ParticleCustomTexture { + + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/leaf_particles.png"); + + public ParticleLeaf(World world, double x, double y, double z, double vx, double vy, double vz){ + super(world, x, y, z, vx, vy, vz); + } + + public ParticleLeaf(World world, double x, double y, double z, double vx, double vy, double vz, int maxAge){ + super(world, x, y, z, vx, vy, vz, maxAge); + } + + @Override + public void init() { + this.setParticleTextureIndex(rand.nextInt(16)); + this.particleScale *= 1.4f; + this.particleGravity = 0; + this.canCollide = true; + } + + @Override public ResourceLocation getTexture(){ return TEXTURE; } + @Override protected int getXFrames(){ return 4; } + @Override protected int getYFrames(){ return 4; } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleMagicFlame.java b/src/main/java/electroblob/wizardry/client/particle/ParticleMagicFlame.java new file mode 100644 index 00000000..755fa4db --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleMagicFlame.java @@ -0,0 +1,47 @@ +package electroblob.wizardry.client.particle; + +import net.minecraft.client.particle.Particle; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.entity.Entity; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ParticleMagicFlame extends Particle { + + /** The scale of the flame particle */ + private float flameScale; + + public ParticleMagicFlame(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, int maxAge, float scale) + { + super(par1World, par2, par4, par6, par8, par10, par12); + this.motionX = this.motionX * 0.009999999776482582D + par8; + this.motionY = this.motionY * 0.009999999776482582D + par10; + this.motionZ = this.motionZ * 0.009999999776482582D + par12; + this.flameScale = scale; + this.particleRed = this.particleGreen = this.particleBlue = 1.0F; + if(maxAge == 0){ + this.particleMaxAge = (int)(2.0D / (Math.random() * 0.8D + 0.2D)); + }else{ + this.particleMaxAge = maxAge; + } + // IDEA: Make the particles for ray spells collide properly and not spawn on the other side of stuff. + this.canCollide = false; + this.setParticleTextureIndex(48); + } + + @Override + public void renderParticle(VertexBuffer buffer, Entity entity, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ) + { + float f6 = ((float)this.particleAge + partialTicks) / (float)this.particleMaxAge; + this.particleScale = this.flameScale * (1.0F - f6 * f6 * 0.5F); + super.renderParticle(buffer, entity, partialTicks, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ); + } + + @Override + public int getBrightnessForRender(float par1) + { + return 256; + } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticlePath.java b/src/main/java/electroblob/wizardry/client/particle/ParticlePath.java new file mode 100644 index 00000000..cf7b2c6e --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticlePath.java @@ -0,0 +1,93 @@ +package electroblob.wizardry.client.particle; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.spell.Clairvoyance; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ParticlePath extends ParticleCustomTexture { + + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/path_particles.png"); + + private final double originX, originY, originZ; + + public ParticlePath(World world, double x, double y, double z, double vx, double vy, double vz, float r, float g, float b){ + super(world, x, y, z, vx, vy, vz); + this.setRBGColorF(r, g, b); + this.originX = x; + this.originY = y; + this.originZ = z; + } + + public ParticlePath(World world, double x, double y, double z, double vx, double vy, double vz, float r, float g, float b, int maxAge){ + super(world, x, y, z, vx, vy, vz, maxAge); + this.setRBGColorF(r, g, b); + this.originX = x; + this.originY = y; + this.originZ = z; + } + + @Override + public void init(){ + this.setParticleTextureIndex(0); + // Set to a constant to remove the randomness from Particle. + this.particleScale = 1.25f; + this.particleGravity = 0; + this.fullBrightness = true; + this.canCollide = false; + } + + @Override public ResourceLocation getTexture(){ return TEXTURE; } + @Override protected int getXFrames(){ return 1; } + @Override protected int getYFrames(){ return 1; } + + @Override + public void onUpdate(){ + + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + + if (this.particleAge++ >= this.particleMaxAge) + { + this.setExpired(); + } + + this.moveEntity(this.motionX, this.motionY, this.motionZ); + + // Fading + if(this.particleAge > this.particleMaxAge / 2){ + this.setAlphaF(1.0F - 2 * (((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge)); + } + + if(this.particleAge % Clairvoyance.PARTICLE_MOVEMENT_INTERVAL == 0){ + this.setPosition(this.originX, this.originY, this.originZ); + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + } + + } + + @Override + public void applyGLStateChanges(){ + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + // TESTME: Are these two actually necessary? + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); + } + + @Override + public void undoGLStateChanges(){ + GlStateManager.disableBlend(); + GlStateManager.enableLighting(); + } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleRotatingSparkle.java b/src/main/java/electroblob/wizardry/client/particle/ParticleRotatingSparkle.java new file mode 100644 index 00000000..eadc09bb --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleRotatingSparkle.java @@ -0,0 +1,59 @@ +package electroblob.wizardry.client.particle; + +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ParticleRotatingSparkle extends ParticleSparkle { + + private double angle; + private double radius; + private double speed; + + public ParticleRotatingSparkle(World world, int maxAge, double originX, double originZ, double radius, double yPos, float r, float g , float b){ + super(world, 0, 0, 0, 0, 0, 0, r, g, b, maxAge); + this.angle = this.rand.nextDouble() * Math.PI * 2; + double x = originX - Math.cos(angle)*radius; + double z = originZ + radius*Math.sin(angle); + this.radius = radius; + this.setPosition(x, yPos, z); + this.prevPosX = x; + this.prevPosY = yPos; + this.prevPosZ = z; + if(rand.nextBoolean()){ + speed = rand.nextDouble()*2 + 1; + }else{ + speed = rand.nextDouble()*-2 - 1; + } + this.multipleParticleScaleBy(1.5f); + } + + @Override + public void onUpdate(){ + + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + + if(this.particleAge++ >= this.particleMaxAge){ + this.setExpired(); + } + + // This is in radians per tick... + double omega = Math.signum(speed) * ((Math.PI*2)/20 - speed/(20*radius)); + + // v = r times omega; therefore the normalised velocity vector needs to be r times the angle increment / 2 pi. + this.angle += omega; + + this.motionY -= 0.04D * (double)this.particleGravity; + this.motionZ = radius * omega * Math.cos(angle); + this.motionX = radius * omega * Math.sin(angle); + this.moveEntity(motionX, motionY, motionZ); + + if(this.particleAge > this.particleMaxAge / 2){ + this.setAlphaF(1.0F - ((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge); + } + + } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleSnow.java b/src/main/java/electroblob/wizardry/client/particle/ParticleSnow.java new file mode 100644 index 00000000..a1455c22 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleSnow.java @@ -0,0 +1,33 @@ +package electroblob.wizardry.client.particle; + +import electroblob.wizardry.Wizardry; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ParticleSnow extends ParticleCustomTexture { + + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/snow_particles.png"); + + public ParticleSnow(World world, double x, double y, double z, double vx, double vy, double vz){ + super(world, x, y, z, vx, vy, vz); + } + + public ParticleSnow(World world, double x, double y, double z, double vx, double vy, double vz, int maxAge){ + super(world, x, y, z, vx, vy, vz, maxAge); + } + + @Override + public void init(){ + this.setParticleTextureIndex(rand.nextInt(8)); + this.particleScale *= 0.6f; + this.particleGravity = 0; + this.canCollide = true; + } + + @Override public ResourceLocation getTexture(){ return TEXTURE; } + @Override protected int getXFrames(){ return 4; } + @Override protected int getYFrames(){ return 4; } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleSpark.java b/src/main/java/electroblob/wizardry/client/particle/ParticleSpark.java new file mode 100644 index 00000000..57965ed2 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleSpark.java @@ -0,0 +1,58 @@ +package electroblob.wizardry.client.particle; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ParticleSpark extends ParticleCustomTexture { + + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/lightning_particles.png"); + + public ParticleSpark(World world, double x, double y, double z, double vx, double vy, double vz){ + // Max age is always 3. + super(world, x, y, z, vx, vy, vz, 3); + } + + @Override + public void init(){ + // Multiplied by 4 because the index works slightly differently for spark particles. + this.setParticleTextureIndex(rand.nextInt(8)*4); + this.particleScale *= 1.4f; + this.fullBrightness = true; + this.canCollide = false; + } + + @Override + public void onUpdate(){ + super.onUpdate(); + // Well this is handy! Looks like vanilla uses the texture index like this too. + this.nextTextureIndexX(); + } + + @Override public ResourceLocation getTexture(){ return TEXTURE; } + @Override protected int getXFrames(){ return 4; } + @Override protected int getYFrames(){ return 8; } + + @Override + public void applyGLStateChanges(){ + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + // TESTME: Are these two actually necessary? + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); + } + + @Override + public void undoGLStateChanges(){ + GlStateManager.disableBlend(); + GlStateManager.enableLighting(); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleSparkle.java b/src/main/java/electroblob/wizardry/client/particle/ParticleSparkle.java new file mode 100644 index 00000000..fbe89146 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleSparkle.java @@ -0,0 +1,96 @@ +package electroblob.wizardry.client.particle; + +import electroblob.wizardry.Wizardry; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ParticleSparkle extends ParticleCustomTexture { + + /* I have now figured out what particle factories are for: they separate out the individual uses of the varargs + * parameter in spawnParticle so they are kept with the particle class. For my purposes, it would be easier to do + * that in the particle spawning method itself. */ + + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/sparkle_particles.png"); + + // NOTE: Uncomment once 2.1.0 is released + //private final float initialRed; + //private final float initialGreen; + //private final float initialBlue; + + // TODO: Assign these via the constructors, as part of the refactoring for particle parameters. + // NOTE: Uncomment once 2.1.0 is released +// private final float fadeRed = 1; +// private final float fadeGreen = 1; +// private final float fadeBlue = 0; + + public ParticleSparkle(World world, double x, double y, double z, double vx, double vy, double vz, float r, float g, float b){ + super(world, x, y, z, vx, vy, vz); + this.setRBGColorF(r, g, b); + // NOTE: Uncomment once 2.1.0 is released + //initialRed = r; + //initialGreen = g; + //initialBlue = b; + this.particleMaxAge = 48 + this.rand.nextInt(12); + } + + public ParticleSparkle(World world, double x, double y, double z, double vx, double vy, double vz, float r, float g, float b, int maxAge){ + super(world, x, y, z, vx, vy, vz, maxAge); + this.setRBGColorF(r, g, b); + // NOTE: Uncomment once 2.1.0 is released + //initialRed = r; + //initialGreen = g; + //initialBlue = b; + } + + public ParticleSparkle(World world, double x, double y, double z, double vx, double vy, double vz, float r, float g, float b, boolean doGravity){ + this(world, x, y, z, vx, vy, vz, r, g, b); + this.particleGravity = doGravity ? 1 : 0; + } + + public ParticleSparkle(World world, double x, double y, double z, double vx, double vy, double vz, float r, float g, float b, int maxAge, boolean doGravity){ + this(world, x, y, z, vx, vy, vz, r, g, b, maxAge); + this.particleGravity = doGravity ? 1 : 0; + } + + @Override + public void init(){ + this.setParticleTextureIndex(rand.nextInt(16)); + this.particleScale *= 0.75f; + this.particleGravity = 0; + this.canCollide = false; + this.fullBrightness = true; + } + + @Override public ResourceLocation getTexture(){ return TEXTURE; } + @Override protected int getXFrames(){ return 4; } + @Override protected int getYFrames(){ return 4; } + + @Override + public void onUpdate(){ + + super.onUpdate(); + // Fading + if(this.particleAge > this.particleMaxAge / 2){ + this.setAlphaF(1.0F - ((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge); + } + // Colour fading TODO Uncomment once 2.1.0 is released +// float ageFraction = (float)this.particleAge / (float)this.particleMaxAge; +// this.setRBGColorF(this.initialRed + (this.fadeRed - this.initialRed)*ageFraction, +// this.initialGreen + (this.fadeGreen - this.initialGreen)*ageFraction, +// this.initialBlue + (this.fadeBlue - this.initialBlue)*ageFraction); +// +// this.setParticleTextureIndex((this.particleAge * 11)/this.particleMaxAge); + } + + /* + * As a side note, I see a lot of magic mods with fancy-looking particle effects that really seem to 'glow'. It's + * actually not that hard - you simply create a reasonably high-res texture with translucency and then set the + * OpenGL blend function to something like SRC_ALPHA, SRC_ALPHA or ONE, ONE. The thing is... they're not very + * Minecraft-y. I still maintain that part of wizardry's appeal is that it stays true to the game's pixelated charm, + * rather than trying to make it something it's not. Still, the newer textures are much better than the defaults I + * used to use. + */ +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleTornado.java b/src/main/java/electroblob/wizardry/client/particle/ParticleTornado.java new file mode 100644 index 00000000..1dd5635b --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/ParticleTornado.java @@ -0,0 +1,83 @@ +package electroblob.wizardry.client.particle; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.particle.ParticleDigging; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ParticleTornado extends ParticleDigging { + + private double angle; + private double radius; + private double speed; + /** Velocity of the tornado itself; in other words the velocity of the point the particle circles around. */ + private double velX, velZ; + private boolean fullBrightness = false; + + public ParticleTornado(World world, int maxAge, double originX, double originZ, double radius, double yPos, double velX, double velZ, IBlockState block){ + super(world, 0, 0, 0, 0, 0, 0, block); + this.angle = this.rand.nextDouble() * Math.PI * 2; + double x = originX - Math.cos(angle)*radius; + double z = originZ + radius*Math.sin(angle); + this.radius = radius; + this.setPosition(x, yPos, z); + this.prevPosX = x; + this.prevPosY = yPos; + this.prevPosZ = z; + //this.particleScale *= 0.75F; + this.particleMaxAge = maxAge; + this.canCollide = false; + // Grass has special treatment, since it has a colourised top but the rest is normal. + // Commented out for now since vanilla does something about this now, but I'm not sure what exactly + //if(block.getBlock() != Blocks.GRASS || side == 1) this.setColour(block.getRenderColor(side)); + + // Blocks that emit light are rendered with full brightness. + if(block.getLightValue(world, new BlockPos(this.posX, this.posY, this.posZ)) == 0){ + this.particleRed *= 0.75; + this.particleGreen *= 0.75; + this.particleBlue *= 0.75; + }else{ + this.fullBrightness = true; + } + + speed = rand.nextDouble()*2 + 1; + this.velX = velX; + this.velZ = velZ; + } + + @Override + public void onUpdate(){ + + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + + if(this.particleAge++ >= this.particleMaxAge){ + this.setExpired(); + } + + // This is in radians per tick... + double omega = Math.signum(speed) * ((Math.PI*2)/20 - speed/(20*radius)); + + // v = r times omega; therefore the normalised velocity vector needs to be r times the angle increment / 2 pi. + this.angle += omega; + + this.motionZ = radius * omega * Math.cos(angle); + this.motionX = radius * omega * Math.sin(angle); + this.moveEntity(motionX + velX, 0, motionZ + velZ); + + if(this.particleAge > this.particleMaxAge / 2){ + this.setAlphaF(1.0F - ((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge); + } + + } + + @Override + public int getBrightnessForRender(float partialTicks){ + return fullBrightness ? 15728880 : super.getBrightnessForRender(partialTicks); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/LayerStone.java b/src/main/java/electroblob/wizardry/client/renderer/LayerStone.java new file mode 100644 index 00000000..4365a842 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/LayerStone.java @@ -0,0 +1,191 @@ +package electroblob.wizardry.client.renderer; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map.Entry; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.ClientProxy; +import electroblob.wizardry.spell.Petrify; +import net.minecraft.client.Minecraft; +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.model.ModelBiped; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.GlStateManager.DestFactor; +import net.minecraft.client.renderer.GlStateManager.SourceFactor; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderLivingBase; +import net.minecraft.client.renderer.entity.RenderZombie; +import net.minecraft.client.renderer.entity.layers.LayerRenderer; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityZombie; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraftforge.fml.relauncher.ReflectionHelper; + +/** + * Layer used to render the stone texture on a petrified creature. Handles dynamic tiling of the stone texture and + * reflective access for classes that don't play nicely (looking at you, {@link RenderZombie}). + * @author Electroblob + * @since Wizardry 1.2 + */ +public class LayerStone implements LayerRenderer { + + protected ModelBase model; + private final RenderLivingBase renderer; + + private static final ResourceLocation texture = new ResourceLocation("textures/blocks/stone.png"); + + private static final Field zombieLayers = ReflectionHelper.findField(RenderZombie.class, "defaultLayers", "field_177122_o"); + private static final Field zombieVillagerLayers = ReflectionHelper.findField(RenderZombie.class, "villagerLayers", "field_177121_n"); + private static final Method swapZombieModel = ReflectionHelper.findMethod(RenderZombie.class, null, + new String[]{"swapArmor", "func_82427_a"}, EntityZombie.class); // Second parameter (null) is unused + + @SuppressWarnings("unchecked") // The compiler is being annoying and I know that what I'm doing is type-safe. + public static void initialiseLayers(){ + for(Entry, Render> entry : Minecraft.getMinecraft().getRenderManager().entityRenderMap.entrySet()){ + // Zombies don't play nicely because they have their own, private lists of layers for the regular zombie + // and the zombie villager, which are assigned within the constructor for RenderZombie and swapped out + // as necessary. However, Mojang, in their infinite wisdom, haven't bothered to override addLayer to + // modify those internal lists, so that method is useless. + if(entry.getValue() instanceof RenderZombie){ + try { + Object layers = zombieLayers.get(entry.getValue()); + if(layers instanceof List){ + // Nice as the layer renderer system is, it doesn't lend itself to reflective access. + // I KNOW that 'layers' (which was obtained using reflection) is of the type + // List>, because that's what it's declared as. However, if I cast + // 'layers' to List>, I can't add a LayerStone because it's only a + // LayerRenderer, not a LayerRenderer. + ((List>)layers).add(new LayerStone((RenderLivingBase)entry.getValue())); + } + layers = zombieVillagerLayers.get(entry.getValue()); + if(layers instanceof List){ + ((List>)layers).add(new LayerStone((RenderLivingBase)entry.getValue())); + } + } catch (IllegalArgumentException | IllegalAccessException e){ + Wizardry.logger.error("Error while reflectively accessing zombie render layers"); + e.printStackTrace(); + } + }else if(entry.getValue() instanceof RenderLivingBase){ + // Adds a stone layer to all the living entity renderers in the game. Whether it is actually rendered + // is decided in doRenderLayer below on a per-entity basis. + ((RenderLivingBase)entry.getValue()).addLayer(new LayerStone((RenderLivingBase)entry.getValue())); + } + // NOTE: May have to do some special stuff for players if they are to be added; see Minecraft.getMinecraft().getRenderManager().getSkinMap() + } + } + + public LayerStone(RenderLivingBase renderer){ + this.renderer = renderer; + this.model = renderer.getMainModel(); + } + + @Override + public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks, float ageInTicks, float netHeadYaw, float headPitch, float scale){ + + if(entity.getEntityData().getBoolean(Petrify.NBT_KEY)){ + + GlStateManager.enableLighting(); + int i = this.getBlockBrightnessForEntity(entity, partialTicks); + + int j = i % 65536; + int k = i / 65536; + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)j / 1.0F, (float)k / 1.0F); + + ResourceLocation breakingTexture = ClientProxy.renderStatue.getBlockBreakingTexture(); + + if(breakingTexture != null){ + // Block breaking animation + // TODO: Spider eyes and enderman eyes (any others?) show through the stone when the block is being broken... + GlStateManager.enableBlend(); + GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA); + this.renderer.bindTexture(breakingTexture); + this.renderEntityModel(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale); + GlStateManager.disableBlend(); + }else{ + // Stone texture + this.renderer.bindTexture(texture); + this.renderEntityModel(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale); + } + } + } + + private int getBlockBrightnessForEntity(Entity entity, float partialTicks){ + + BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(MathHelper.floor_double(entity.posX), 0, MathHelper.floor_double(entity.posZ)); + + if(entity.worldObj.isBlockLoaded(pos)){ + pos.setY(MathHelper.floor_double(entity.posY + (double)entity.getEyeHeight())); + return entity.worldObj.getCombinedLight(pos, 0); + }else{ + return 0; + } + } + + private void renderEntityModel(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks, float ageInTicks, float netHeadYaw, float headPitch, float scale){ + + GlStateManager.pushMatrix(); + // Enables tiling (Also used for guardian beam, beacon beam and ender crystal beam) + // TODO: Backport this improvement + GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT); + GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT); + + GlStateManager.depthMask(true); // Some entities set depth mask to false (i.e. no sorting of faces by depth) + // In particular, LayerSpiderEyes sets it to false when the spider is invisible, for some reason. + + // Changes the scale at which the texture is applied to the model. See LayerCreeper for a similar example, + // but with translation instead of scaling. + GlStateManager.matrixMode(GL11.GL_TEXTURE); + GlStateManager.loadIdentity(); + double scaleX = 1, scaleY = 1; + // It's more logical to use the model's texture size, but some classes don't bother setting it properly + // (e.g. ModelVillager), so to get the correct dimensions I'm getting them from the first box instead. + if(model.boxList != null && model.boxList.get(0) != null){ + scaleX = (double)model.boxList.get(0).textureWidth/16d; + scaleY = (double)model.boxList.get(0).textureHeight/16d; + }else{ // Fallback to model fields; should never be needed + scaleX = (double)model.textureWidth/16d; + scaleY = (double)model.textureHeight/16d; + } + GlStateManager.scale(scaleX, scaleY, 1); + GlStateManager.matrixMode(GL11.GL_MODELVIEW); + + // Lets RenderZombie do its (stupid and inflexible) model switching thing + if(this.renderer instanceof RenderZombie && entity instanceof EntityZombie){ + try { + swapZombieModel.invoke(this.renderer, entity); + this.model = this.renderer.getMainModel(); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + Wizardry.logger.error("Error while reflectively calling RenderZombie#swapArmor"); + e.printStackTrace(); + } + } + // Hides the hat layer for bipeds + if(this.model instanceof ModelBiped) ((ModelBiped) this.model).bipedHeadwear.isHidden = true; + + this.model.setLivingAnimations(entity, limbSwing, limbSwingAmount, partialTicks); + this.model.render(entity, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale); + + if(this.model instanceof ModelBiped) ((ModelBiped) this.model).bipedHeadwear.isHidden = false; + + // Undoes the texture scaling + GlStateManager.matrixMode(GL11.GL_TEXTURE); + GlStateManager.loadIdentity(); + GlStateManager.matrixMode(GL11.GL_MODELVIEW); + + GlStateManager.popMatrix(); + } + + @Override + public boolean shouldCombineTextures(){ + return false; + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/renderer/RayHelper.java b/src/main/java/electroblob/wizardry/client/renderer/RayHelper.java new file mode 100644 index 00000000..0fdd1e1e --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RayHelper.java @@ -0,0 +1,48 @@ +package electroblob.wizardry.client.renderer; + +/** Instances of this class can calculate their distance from the camera viewpoint and sort themselves accordingly. */ +class RayHelper implements Comparable { + + int ordinal; + double x1, y1, z1; + double x2, y2, z2; + double offsetX, offsetY, offsetZ; + + RayHelper(int ordinal, double x1, double y1, double z1, double x2, double y2, double z2, double offsetX, double offsetY, double offsetZ){ + this.ordinal = ordinal; + this.x1 = x1; + this.y1 = y1; + this.z1 = z1; + this.x2 = x2; + this.y2 = y2; + this.z2 = z2; + this.offsetX = offsetX; + this.offsetY = offsetY; + this.offsetZ = offsetZ; + } + + double getDistanceFromViewpoint(){ + + double midX = (x1+x2)/2; + double midY = (y1+y2)/2; + double midZ = (z1+z2)/2; + + double absoluteX = offsetX+midX; + double absoluteY = offsetY+midY; + double absoluteZ = offsetZ+midZ; + + return Math.sqrt(absoluteX*absoluteX + absoluteY*absoluteY + absoluteZ*absoluteZ); + } + + @Override + public int compareTo(RayHelper ray){ + if(this.getDistanceFromViewpoint() > ray.getDistanceFromViewpoint()){ + return -1; + }else if(this.getDistanceFromViewpoint() < ray.getDistanceFromViewpoint()){ + return 1; + }else{ + return 0; + } + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderArc.java b/src/main/java/electroblob/wizardry/client/renderer/RenderArc.java new file mode 100644 index 00000000..6bf600d1 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderArc.java @@ -0,0 +1,129 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.EntityArc; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.ResourceLocation; + +public class RenderArc extends Render { + + private static final ResourceLocation[] textures = new ResourceLocation[16]; + + public RenderArc(RenderManager renderManager){ + super(renderManager); + for(int i=0;i<16;i++){ + textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/arc_" + i + ".png"); + } + } + + @Override + public void doRender(EntityArc arc, double d0, double d1, double d2, + float fa, float fb) { + GlStateManager.pushMatrix(); + GlStateManager.translate((float)d0, (float)d1, (float)d2); + GlStateManager.disableLighting(); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); //This line fixes the weird brightness bug. + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + + //System.out.println("Entity coords: " + entity.posX + ", " + entity.posY + ", " + entity.posZ); + //System.out.println("doRender parameters: " + d0 + ", " + d1 + ", " + d2 + ", " + fa + ", " + fb); + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + + bindTexture(textures[arc.textureIndex]); // This MUST be after the tessellator declaration and the gl stuff + + /** + * Note: A lot of the maths here works on similar triangles and the ratios between them, avoiding too much + * pythagoras and eliminating the need for any trig. Ratios are used for the positioning of the arc endpoints. + * Ratios are usually used swapping x and z because the triangles are rotated through 90 degrees. + */ + + double dx = -d0; + double dy = -d1; + double dz = -d2; + + if(arc.x1 != 0){ + dx = arc.x1 - arc.posX;// - d2/lengthOffsetRatio; + dy = arc.y1 - arc.posY + 0.3; + dz = arc.z1 - arc.posZ;// + d0/lengthOffsetRatio; + + + //The distance from caster to target + double arcLength = Math.sqrt(dz*dz+dx*dx); + + //The ratio between the length of the arc and the offset of the start point from the player's centre (which is always 0.3). + //double lengthOffsetRatio = arcLength/0.3; + + //EntityClientPlayerMP player = Minecraft.getMinecraft().thePlayer; + + //double xViewDist = player.posX - d0; + //double yViewDist = player.posY + player.eyeHeight - d1; + //double zViewDist = player.posZ - d2; + + //double xzViewDist = Math.sqrt(xViewDist * xViewDist + zViewDist * zViewDist); + + //The angle above the horizontal that this particular player is viewing the arc from + //double viewAngle = Math.atan(yViewDist/xzViewDist); + + //Half the width of the arc + double arcWidth = 0.3d; + + //Right hand side of vertical plane + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + //Target end + buffer.pos(0, -0.5, 0).tex(1, 1).endVertex(); + buffer.pos(0, 0.5, 0).tex(1, 0).endVertex(); + //Caster end + buffer.pos(dx, dy, dz).tex(0, 0).endVertex(); + buffer.pos(dx, dy-1, dz).tex(0, 1).endVertex(); + tessellator.draw(); + + //Left + buffer.begin(GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); + //Target end + buffer.pos(0, -0.5, 0).tex(1, 1).endVertex(); + //Caster end + buffer.pos(dx, dy-1, dz).tex(0, 1).endVertex(); + buffer.pos(dx, dy, dz).tex(0, 0).endVertex(); + //Target end + buffer.pos(0, 0.5, 0).tex(1, 0).endVertex(); + tessellator.draw(); + + //Bottom of horizontal plane + buffer.begin(GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); + buffer.pos((arcWidth/arcLength)*dz, 0, (-arcWidth/arcLength)*dx).tex(1, 1).endVertex(); + buffer.pos(dx + (arcWidth/arcLength)*dz, dy-0.5, dz - (arcWidth/arcLength)*dx).tex(0, 1).endVertex(); + buffer.pos(dx - (arcWidth/arcLength)*dz, dy-0.5, dz + (arcWidth/arcLength)*dx).tex(0, 0).endVertex(); + buffer.pos((-arcWidth/arcLength)*dz, 0, (arcWidth/arcLength)*dx).tex(1, 0).endVertex(); + tessellator.draw(); + + //Top + buffer.begin(GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); + buffer.pos((arcWidth/arcLength)*dz, 0, (-arcWidth/arcLength)*dx).tex(1, 1).endVertex(); + buffer.pos((-arcWidth/arcLength)*dz, 0, (arcWidth/arcLength)*dx).tex(1, 0).endVertex(); + buffer.pos(dx - (arcWidth/arcLength)*dz, dy-0.5, dz + (arcWidth/arcLength)*dx).tex(0, 0).endVertex(); + buffer.pos(dx + (arcWidth/arcLength)*dz, dy-0.5, dz - (arcWidth/arcLength)*dx).tex(0, 1).endVertex(); + tessellator.draw(); + } + + GlStateManager.enableLighting(); + GlStateManager.disableBlend(); + GlStateManager.popMatrix(); + } + + @Override + protected ResourceLocation getEntityTexture(EntityArc entity) { + return textures[entity.textureIndex]; + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneWorkbench.java new file mode 100644 index 00000000..12203694 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneWorkbench.java @@ -0,0 +1,99 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; +import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType; +import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; + +public class RenderArcaneWorkbench extends TileEntitySpecialRenderer { + + private static final ResourceLocation runeTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/rune.png"); + + public RenderArcaneWorkbench(){} + + @Override + public void renderTileEntityAt(TileEntityArcaneWorkbench tileentity, double x, double y, double z, float partialTicks, int destroyStage) { + + GlStateManager.pushMatrix(); + // This line makes stuff render in the same place relative to the world wherever the player is. + GlStateManager.translate((float)x + 0.5F, (float)y + 1.5F, (float)z + 0.5F); + GlStateManager.rotate(180, 0F, 0F, 1F); + GlStateManager.pushMatrix(); + double angle = 0.0d; + if(x < -0.5){ + angle = Math.toDegrees(Math.atan((z+0.5)/(x+0.5))) + 180; + }else{ + angle = Math.toDegrees(Math.atan((z+0.5)/(x+0.5))); + } + this.renderEffect(tileentity); + this.renderWand(tileentity, angle); + GlStateManager.popMatrix(); + GlStateManager.popMatrix(); + } + + private void renderEffect(TileEntityArcaneWorkbench tileentity) { + + ItemStack itemstack = tileentity.getStackInSlot(ContainerArcaneWorkbench.WAND_SLOT); + + if(itemstack != null){ + GlStateManager.pushMatrix(); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); //This line fixes the weird brightness bug. + GlStateManager.rotate(tileentity.timer, 0.0f, 1.0f, 0.0f); + GlStateManager.translate(0.0f, 0.65f, 0.0f); + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + bindTexture(runeTexture); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + buffer.pos(-0.5f, 0, -0.5f).tex(0, 0).endVertex(); + buffer.pos(0.5f, 0, -0.5f).tex(1, 0).endVertex(); + buffer.pos(0.5f, 0, 0.5f).tex(1, 1).endVertex(); + buffer.pos(-0.5f, 0, 0.5f).tex(0, 1).endVertex(); + tessellator.draw(); + + GlStateManager.disableBlend(); + GlStateManager.enableLighting(); + GlStateManager.popMatrix(); + } + } + + /** + * Renders the wand on the workbench as 3D on the model. Currently doesn't do much on 'fast' graphics! + * @param tileentity The instance of the workbench tile entity + */ + private void renderWand(TileEntityArcaneWorkbench tileentity, double viewAngle) + { + ItemStack stack = tileentity.getStackInSlot(ContainerArcaneWorkbench.WAND_SLOT); + + if(stack != null){ + + GlStateManager.pushMatrix(); + GlStateManager.rotate(180.0F, 1.0F, 0.0F, 0.0F); + GlStateManager.rotate(90.0F, 1.0F, 0.0F, 0.0F); + + GlStateManager.rotate(180, 0, 1, 0); + // View angle is negated because of the 180 flip. + GlStateManager.rotate((float)(-viewAngle-90f), 0, 0, 1); + // Does the floaty thing + GlStateManager.translate(0.0F, 0.0F, (float)tileentity.yOffset/5000.0F - 0.55f); + GlStateManager.scale(0.75F, 0.75F, 0.75F); + // This is what the item frame uses so it's definitely what we want. + Minecraft.getMinecraft().getRenderItem().renderItem(stack, TransformType.FIXED); + GlStateManager.popMatrix(); + } + } +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderBlackHole.java b/src/main/java/electroblob/wizardry/client/renderer/RenderBlackHole.java new file mode 100644 index 00000000..67cdd6e8 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderBlackHole.java @@ -0,0 +1,180 @@ +package electroblob.wizardry.client.renderer; + +import java.util.ArrayList; +import java.util.Collections; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.construct.EntityBlackHole; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.ResourceLocation; + +public class RenderBlackHole extends Render { + + private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/dark_ray.png"); + private static final ResourceLocation texture2 = new ResourceLocation(Wizardry.MODID, "textures/entity/black_hole.png"); + + public RenderBlackHole(RenderManager renderManager) { + super(renderManager); + } + + @Override + public void doRender(EntityBlackHole blackhole, double x, double y, double z, float fa, float fb) { + + GlStateManager.pushMatrix(); + + GlStateManager.disableCull(); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GlStateManager.shadeModel(GL11.GL_SMOOTH); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + RenderHelper.disableStandardItemLighting(); + + GlStateManager.translate(x, y, z); + + //float pitch = (float) Math.toDegrees(Math.atan(y/(x*x+z*z))); + //float yaw = (float) Math.toDegrees(Math.atan(x/z)); + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + if(blackhole.ticksExisted < 10){ + GlStateManager.scale((float)blackhole.ticksExisted/10, (float)blackhole.ticksExisted/10, (float)blackhole.ticksExisted/10); + } + if(blackhole.ticksExisted > blackhole.lifetime - 10){ + GlStateManager.scale((float)(blackhole.lifetime-blackhole.ticksExisted)/10, (float)(blackhole.lifetime-blackhole.ticksExisted)/10, (float)(blackhole.lifetime-blackhole.ticksExisted)/10); + } + + this.bindTexture(texture); + + // In theory this stuff should sort the rays into the correct render order based on the distance from + // the 'camera' (i.e. the player's viewpoint) + // In the end it was easier to do away with the openGL rotation because for some reason it produced + // coordinates which were inconsistent with my calculated ones. Since I know those will give the desired + // effect anyway, I just used them directly instead. + + ArrayList rays = new ArrayList(1); + + for(int j=0; j<30; j++){ + + float scale = 3.0f; + + int a = blackhole.randomiser[j]; + int b = blackhole.randomiser2[j]; + + int sliceAngle = 20 + a; + + double x1 = scale*Math.sin((blackhole.ticksExisted + 40*j)*(Math.PI/180)); + //double y1 = 0.7*Math.cos((blackhole.timer - 40*j)*(Math.PI/180))*j/10; + double z1 = scale*Math.cos((blackhole.ticksExisted + 40*j)*(Math.PI/180)); + + double x2 = scale*Math.sin((blackhole.ticksExisted + 40*j - sliceAngle)*(Math.PI/180)); + //double y2 = 0.7*Math.sin((blackhole.timer - 40*j)*(Math.PI/180))*j/10; + double z2 = scale*Math.cos((blackhole.ticksExisted + 40*j - sliceAngle)*(Math.PI/180)); + + double absoluteX = x1*Math.cos(31*b); + double absoluteY = z1*Math.sin(31*a) + x1*Math.cos(31*a)*Math.sin(31*b); + double absoluteZ = z1*Math.cos(31*a); + + double absoluteX2 = x2*Math.cos(31*b); + double absoluteY2 = z2*Math.sin(31*a) + x2*Math.cos(31*a)*Math.sin(31*b); + double absoluteZ2 = z2*Math.cos(31*a); + /* + buffer.begin(0, DefaultVertexFormats.POSITION_TEX); + + tessellator.setColorOpaque(255, 255, 255); + GL11.glPointSize(5); + + tessellator.addVertex(absoluteX-x, 0, 0); + tessellator.addVertex(0, absoluteY-y, 0); + tessellator.addVertex(0, 0, absoluteZ-z); + + tessellator.draw(); + */ + rays.add(new RayHelper(j, absoluteX, absoluteY, absoluteZ, absoluteX2, absoluteY2, absoluteZ2, x, y, z)); + + } + + Collections.sort(rays); + + for(RayHelper ray : rays){ + + GlStateManager.pushMatrix(); + //GlStateManager.rotate(31*blackhole.randomiser[ray.ordinal], 1, 0, 0); + //GlStateManager.rotate(31*blackhole.randomiser2[ray.ordinal], 0, 0, 1); + + buffer.begin(5, DefaultVertexFormats.POSITION_TEX); + + //tessellator.setColorRGBA(255, 255, 255, 0); + buffer.pos(0, 0, 0).tex(0, 0).endVertex(); + buffer.pos(0, 0, 0).tex(0, 1).endVertex(); + + //tessellator.setColorRGBA(0, 0, 0, 255); + buffer.pos(ray.x1, ray.y1, ray.z1).tex(1, 0).endVertex(); + buffer.pos(ray.x2, ray.y2, ray.z2).tex(1, 1).endVertex(); + + tessellator.draw(); + + GlStateManager.popMatrix(); + } + + GlStateManager.pushMatrix(); + + /* Deprecated in favour of particle style method. + GlStateManager.rotate(yaw, 0, 1, 0); + // GL transformations are relative, hence only x rotation + if(z < 0){ + GlStateManager.rotate(pitch, 1, 0, 0); + }else{ + GlStateManager.rotate(-1*pitch, 1, 0, 0); + } + */ + + // Renders the aura effect + + // This counteracts the reverse rotation behaviour when in front f5 view. Vanilla now has this fix too. + float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2 ? this.renderManager.playerViewX : -this.renderManager.playerViewX; + GlStateManager.rotate(180.0F - this.renderManager.playerViewY, 0.0F, 1.0F, 0.0F); + GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + this.bindTexture(texture2); + + buffer.pos(-0.4, 0.4, 0).tex(0, 0).endVertex(); + buffer.pos(0.4, 0.4, 0).tex(1, 0).endVertex(); + buffer.pos(0.4, -0.4, 0).tex(1, 1).endVertex(); + buffer.pos(-0.4, -0.4, 0).tex(0, 1).endVertex(); + + tessellator.draw(); + + GlStateManager.popMatrix(); + + + GlStateManager.shadeModel(GL11.GL_FLAT); + GlStateManager.enableCull(); + GlStateManager.disableBlend(); + GlStateManager.enableLighting(); + RenderHelper.enableStandardItemLighting(); + + GlStateManager.popMatrix(); + } + + @Override + protected ResourceLocation getEntityTexture(EntityBlackHole entity) { + return texture; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderBlank.java b/src/main/java/electroblob/wizardry/client/renderer/RenderBlank.java new file mode 100644 index 00000000..01926ccc --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderBlank.java @@ -0,0 +1,25 @@ +package electroblob.wizardry.client.renderer; + +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.entity.Entity; +import net.minecraft.util.ResourceLocation; + +public class RenderBlank extends Render { + + public RenderBlank(RenderManager renderManager) { + super(renderManager); + } + + @Override + public void doRender(Entity entity, double d0, double d1, double d2, + float f, float f1) { + + } + + @Override + protected ResourceLocation getEntityTexture(Entity entity) { + return null; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderBubble.java b/src/main/java/electroblob/wizardry/client/renderer/RenderBubble.java new file mode 100644 index 00000000..d96a1020 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderBubble.java @@ -0,0 +1,104 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.construct.EntityBubble; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.ResourceLocation; + +public class RenderBubble extends Render { + + private static final ResourceLocation particleTextures = new ResourceLocation("textures/particle/particles.png"); + private static final ResourceLocation darkOrbTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/dark_orb.png"); + + public RenderBubble(RenderManager renderManager) { + super(renderManager); + } + + @Override + public void doRender(EntityBubble entity, double par2, double par4, double par6, float par8, float par9){ + + GlStateManager.pushMatrix(); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + float yOffset = 0; + + if(WizardryUtilities.getRider(entity) != null){ + yOffset = WizardryUtilities.getRider(entity).height/2; + } + + GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6); + + this.bindTexture(((EntityBubble)entity).isDarkOrb ? darkOrbTexture : particleTextures); + float f6 = 1.0F; + float f7 = 0.5F; + float f8 = 0.5F; + + if(((EntityBubble)entity).isDarkOrb){ + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); + }else{ + int j = entity.getBrightnessForRender(par9); + int k = j % 65536; + int l = j / 65536; + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)k / 1.0F, (float)l / 1.0F); + } + + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + + // This counteracts the reverse rotation behaviour when in front f5 view. + // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. + float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2 ? this.renderManager.playerViewX : -this.renderManager.playerViewX; + GlStateManager.rotate(180.0F - this.renderManager.playerViewY, 0.0F, 1.0F, 0.0F); + GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); + + float f11 = 3.0F; + GlStateManager.scale(f11, f11, f11); + + double pixelwidth = (1.0d/128); + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + //tessellator.setColorRGBA_I(k1, 128); + //buffer.normal(0.0F, 1.0F, 0.0F); + + if(((EntityBubble)entity).isDarkOrb){ + buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.0D).tex(0, 1).endVertex(); + buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.0D).tex(1, 1).endVertex(); + buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.0D).tex(1, 0).endVertex(); + buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.0D).tex(0, 0).endVertex(); + }else{ + buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.0D).tex(pixelwidth, pixelwidth * 24).endVertex(); + buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.0D).tex(pixelwidth*8, pixelwidth * 24).endVertex(); + buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.0D).tex(pixelwidth*8, pixelwidth * 17).endVertex(); + buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.0D).tex(pixelwidth, pixelwidth * 17).endVertex(); + } + + tessellator.draw(); + + GlStateManager.disableBlend(); + if(((EntityBubble)entity).isDarkOrb){ + GlStateManager.enableLighting(); + } + GlStateManager.disableRescaleNormal(); + GlStateManager.popMatrix(); + + } + + @Override + protected ResourceLocation getEntityTexture(EntityBubble entity) { + return null; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderDecay.java b/src/main/java/electroblob/wizardry/client/renderer/RenderDecay.java new file mode 100644 index 00000000..f849f967 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderDecay.java @@ -0,0 +1,74 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.construct.EntityDecay; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.ResourceLocation; + +public class RenderDecay extends Render { + + private static final ResourceLocation[] textures = new ResourceLocation[10]; + + public RenderDecay(RenderManager renderManager){ + super(renderManager); + for(int i=0;i<10;i++){ + textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/decay_" + i + ".png"); + } + } + + @Override + public void doRender(EntityDecay entity, double par2, double par4, double par6, float par8, float par9){ + + GlStateManager.pushMatrix(); + GlStateManager.enableBlend(); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + float yOffset = 0; + + GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6); + + this.bindTexture(textures[((EntityDecay)entity).textureIndex]); + float f6 = 1.0F; + float f7 = 0.5F; + float f8 = 0.5F; + + GlStateManager.rotate(-90, 1, 0, 0); + + float scale = 2*Math.min(1, (float)(EntityDecay.LIFETIME - entity.ticksExisted)/50f); + + GlStateManager.scale(scale, scale, scale); + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + //tessellator.setColorRGBA_I(k1, 128); + //buffer.normal(0.0F, 1.0F, 0.0F); + buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.01).tex(0, 1).endVertex(); + buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.01).tex(1, 1).endVertex(); + buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.01).tex(1, 0).endVertex(); + buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.01).tex(0, 0).endVertex(); + + tessellator.draw(); + + GlStateManager.disableBlend(); + GlStateManager.enableLighting(); + GlStateManager.disableRescaleNormal(); + GlStateManager.popMatrix(); + } + + @Override + protected ResourceLocation getEntityTexture(EntityDecay entity) { + return null; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderDecoy.java b/src/main/java/electroblob/wizardry/client/renderer/RenderDecoy.java new file mode 100644 index 00000000..8e373b04 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderDecoy.java @@ -0,0 +1,108 @@ +package electroblob.wizardry.client.renderer; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.living.EntityDecoy; +import net.minecraft.client.model.ModelBiped; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderBiped; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.entity.Entity; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.ReflectionHelper; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +// TODO: Backport the rewrite of this entire class. +@SideOnly(Side.CLIENT) +public class RenderDecoy extends RenderBiped { + + private static final ResourceLocation steveTextures = new ResourceLocation("textures/entity/steve.png"); + + private static final Method getEntityTexture = ReflectionHelper.findMethod(Render.class, null, + new String[]{"getEntityTexture", "func_110775_a"}, Entity.class); // Generic parameter T is erased to Entity at runtime. + + public RenderDecoy(RenderManager manager){ + super(manager, new ModelBiped(0.0f), 0.5f); + } + + @Override + public void doRender(EntityDecoy entity, double x, double y, double z, float entityYaw, float partialTicks) { + + if(entity.getCaster() != null){ + + this.renderName(entity, x, y, z); + + // Save relevant animation fields from the caster to local variables + float pitch = entity.getCaster().rotationPitch; + float prevPitch = entity.getCaster().prevRotationPitch; + float swing = entity.getCaster().swingProgress; + float prevSwing = entity.getCaster().prevSwingProgress; + float yawOffset = entity.getCaster().renderYawOffset; + float prevYawOffset = entity.getCaster().prevRenderYawOffset; + float yaw = entity.getCaster().rotationYawHead; + float prevYaw = entity.getCaster().prevRotationYawHead; + float limbSwing = entity.getCaster().limbSwing; + float limbSwingAmount = entity.getCaster().limbSwingAmount; + float prevLimbSwingAmount = entity.getCaster().prevLimbSwingAmount; + int hurtTime = entity.getCaster().hurtTime; + boolean sneak = entity.getCaster().isSneaking(); + Entity mount = entity.getCaster().getRidingEntity(); + + // Assign decoy's animation fields to the caster + entity.getCaster().rotationPitch = entity.rotationPitch; + entity.getCaster().prevRotationPitch = entity.prevRotationPitch; + entity.getCaster().swingProgress = entity.swingProgress; + entity.getCaster().prevSwingProgress = entity.prevSwingProgress; + entity.getCaster().renderYawOffset = entity.renderYawOffset; + entity.getCaster().prevRenderYawOffset = entity.prevRenderYawOffset; + entity.getCaster().rotationYawHead = entity.rotationYawHead; + entity.getCaster().prevRotationYawHead = entity.prevRotationYawHead; + entity.getCaster().limbSwing = entity.limbSwing; + entity.getCaster().limbSwingAmount = entity.limbSwingAmount; + entity.getCaster().prevLimbSwingAmount = entity.prevLimbSwingAmount; + entity.getCaster().hurtTime = entity.hurtTime; + entity.getCaster().setSneaking(false); // Decoys can't sneak FIXME Not working! + entity.getCaster().dismountRidingEntity(); // Decoys can't ride anything + + // Do the rendering + renderManager.getEntityRenderObject(entity.getCaster()).doRender(entity.getCaster(), x, y, z, entityYaw, partialTicks); + + // Reset caster's animation fields to their original values + entity.getCaster().rotationPitch = pitch; + entity.getCaster().prevRotationPitch = prevPitch; + entity.getCaster().swingProgress = swing; + entity.getCaster().prevSwingProgress = prevSwing; + entity.getCaster().renderYawOffset = yawOffset; + entity.getCaster().prevRenderYawOffset = prevYawOffset; + entity.getCaster().rotationYawHead = yaw; + entity.getCaster().prevRotationYawHead = prevYaw; + entity.getCaster().limbSwing = limbSwing; + entity.getCaster().limbSwingAmount = limbSwingAmount; + entity.getCaster().prevLimbSwingAmount = prevLimbSwingAmount; + entity.getCaster().hurtTime = hurtTime; + entity.getCaster().setSneaking(sneak); + if(mount != null) entity.getCaster().startRiding(mount); + + }else{ + super.doRender(entity, x, y, z, entityYaw, partialTicks); + } + } + + @Override + protected ResourceLocation getEntityTexture(EntityDecoy entity){ + if(entity.getCaster() != null){ + try { + return (ResourceLocation)getEntityTexture.invoke(renderManager.getEntityRenderObject(entity.getCaster())); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | ClassCastException e){ + Wizardry.logger.error("Error while reflectively calling Render#getEntityTexture as part of decoy rendering"); + e.printStackTrace(); + } + } + // Fallback to steve textures + return steveTextures; + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderEvilWizard.java b/src/main/java/electroblob/wizardry/client/renderer/RenderEvilWizard.java new file mode 100644 index 00000000..d61329f4 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderEvilWizard.java @@ -0,0 +1,34 @@ +package electroblob.wizardry.client.renderer; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.model.ModelWizard; +import electroblob.wizardry.entity.living.EntityEvilWizard; +import net.minecraft.client.renderer.entity.RenderBiped; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.entity.layers.LayerBipedArmor; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class RenderEvilWizard extends RenderBiped +{ + static final ResourceLocation[] textures = new ResourceLocation[6]; + + public RenderEvilWizard(RenderManager renderManager){ + + super(renderManager, new ModelWizard(), 0.5F); + + for(int i=0;i<6;i++){ + textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/evil_wizard_" + i + ".png"); + } + // Just using the default without overriding models, since the armour sets its own model anyway. + this.addLayer(new LayerBipedArmor(this)); + } + + @Override + protected ResourceLocation getEntityTexture(EntityEvilWizard wizard) { + return textures[wizard.textureIndex]; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderFireRing.java b/src/main/java/electroblob/wizardry/client/renderer/RenderFireRing.java new file mode 100644 index 00000000..1f70169f --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderFireRing.java @@ -0,0 +1,180 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.entity.construct.EntityFireRing; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.init.Blocks; +import net.minecraft.util.ResourceLocation; + +public class RenderFireRing extends Render { + + private final ResourceLocation texture; + private float scale = 1.0f; + + public RenderFireRing(RenderManager renderManager, ResourceLocation texture, float scale) { + super(renderManager); + this.texture = texture; + this.scale = scale; + } + + @Override + public void doRender(EntityFireRing entity, double par2, double par4, double par6, float par8, float par9){ + + GlStateManager.pushMatrix(); + GlStateManager.enableBlend(); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + float yOffset = 0; + + GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6); + + this.bindTexture(texture); + float f6 = 1.0F; + float f7 = 0.5F; + float f8 = 0.5F; + + GlStateManager.rotate(-90, 1, 0, 0); + + GlStateManager.scale(scale, scale, scale); + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.01).tex(0, 1).endVertex(); + buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.01).tex(1, 1).endVertex(); + buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.01).tex(1, 0).endVertex(); + buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.01).tex(0, 0).endVertex(); + + tessellator.draw(); + + GlStateManager.disableBlend(); + GlStateManager.enableLighting(); + GlStateManager.disableRescaleNormal(); + GlStateManager.popMatrix(); + + // Fire + + GlStateManager.disableLighting(); + TextureAtlasSprite icon = Minecraft.getMinecraft().getBlockRendererDispatcher().getModelForState(Blocks.FIRE.getDefaultState()).getParticleTexture(); + int sides = 16; + float height = 1.0f; + + for(int k=0; k 0.0F){ + + this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); + float f71 = icon.getMinU(); + float f81 = icon.getMinV(); + float f9 = icon.getMaxU(); + float f10 = icon.getMaxV(); + + if (i / 2 % 2 == 0) + { + float f11 = f9; + f9 = f71; + f71 = f11; + } + + buffer.pos((double)(f2 - f3), (double)(0.0F - f5), (double)f61).tex((double)f9, (double)f10).endVertex(); + buffer.pos((double)(-f2 - f3), (double)(0.0F - f5), (double)f61).tex((double)f71, (double)f10).endVertex(); + buffer.pos((double)(-f2 - f3), (double)(height - f5), (double)f61).tex((double)f71, (double)f81).endVertex(); + buffer.pos((double)(f2 - f3), (double)(height - f5), (double)f61).tex((double)f9, (double)f81).endVertex(); + f4 -= 0.45F; + f5 -= 0.45F; + f2 *= 0.9F; + f61 += 0.03F; + ++i; + } + + tessellator.draw(); + GlStateManager.popMatrix(); + } + + for(int k=0; k 0.0F){ + + this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); + float f71 = icon.getMinU(); + float f81 = icon.getMinV(); + float f9 = icon.getMaxU(); + float f10 = icon.getMaxV(); + + if (i / 2 % 2 == 0) + { + float f11 = f9; + f9 = f71; + f71 = f11; + } + + buffer.pos((double)(f2 - f3), (double)(0.0F - f5), (double)f61).tex((double)f9, (double)f10).endVertex(); + buffer.pos((double)(-f2 - f3), (double)(0.0F - f5), (double)f61).tex((double)f71, (double)f10).endVertex(); + buffer.pos((double)(-f2 - f3), (double)(height - f5), (double)f61).tex((double)f71, (double)f81).endVertex(); + buffer.pos((double)(f2 - f3), (double)(height - f5), (double)f61).tex((double)f9, (double)f81).endVertex(); + f4 -= 0.45F; + f5 -= 0.45F; + f2 *= 0.9F; + f61 += 0.03F; + ++i; + } + + tessellator.draw(); + GlStateManager.popMatrix(); + } + + GlStateManager.enableLighting(); + } + + @Override + protected ResourceLocation getEntityTexture(EntityFireRing entity) { + return null; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderForceArrow.java b/src/main/java/electroblob/wizardry/client/renderer/RenderForceArrow.java new file mode 100644 index 00000000..db37b23d --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderForceArrow.java @@ -0,0 +1,127 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.projectile.EntityForceArrow; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.MathHelper; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class RenderForceArrow extends Render{ + + private static final ResourceLocation arrowTextures = new ResourceLocation(Wizardry.MODID, "textures/entity/force_arrow.png"); + + public RenderForceArrow(RenderManager renderManager){ + super(renderManager); + } + + @Override + public void doRender(EntityForceArrow arrow, double par2, double par4, double par6, float par8, float par9){ + + this.bindEntityTexture(arrow); + GlStateManager.pushMatrix(); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + GlStateManager.translate((float)par2, (float)par4, (float)par6); + GlStateManager.rotate(arrow.prevRotationYaw + (arrow.rotationYaw - arrow.prevRotationYaw) * par9 - 90.0F, 0.0F, 1.0F, 0.0F); + GlStateManager.rotate(arrow.prevRotationPitch + (arrow.rotationPitch - arrow.prevRotationPitch) * par9, 0.0F, 0.0F, 1.0F); + GlStateManager.rotate(180, 0, 1, 0); + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + float pixel = 1.0f/32.0f; + float u1 = 0.0f; + float u2 = pixel*14; + float v1 = 0.0f; + float v2 = pixel*7; + float u3 = pixel*16; + float u4 = 1.0f; + float v3 = 0.0f; + float v4 = pixel*16; + float u5 = 0.0f; + float u6 = pixel*7; + float v5 = pixel*25; + float v6 = 1.0f; + float scale = 0.05625F; + float f11 = 0.0f; + GlStateManager.enableRescaleNormal(); + //f11 = (float)par1EntityArrow.arrowShake - par9; + if (f11 > 0.0F) + { + float f12 = -MathHelper.sin(f11 * 3.0F) * f11; + GlStateManager.rotate(f12, 0.0F, 0.0F, 1.0F); + } + + scale*=0.8f; + + GlStateManager.rotate(45.0F, 1.0F, 0.0F, 0.0F); + GlStateManager.scale(scale, scale, scale); + GlStateManager.translate(-4.0F, 0.0F, 0.0F); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + buffer.pos(-5, 3.5, -3.5).tex((double)u5, (double)v5).endVertex(); + buffer.pos(-5, 3.5, 3.5).tex((double)u6, (double)v5).endVertex(); + buffer.pos(-5, -3.5, 3.5).tex((double)u6, (double)v6).endVertex(); + buffer.pos(-5, -3.5, -3.5).tex((double)u5, (double)v6); + tessellator.draw(); + + for(int i=0; i<5; i++){ + GlStateManager.color(1, 1, 1, 1 - i*0.2f); + double j = i + ((double)arrow.ticksExisted%3)/3; + double width = 2.0d + (Math.sqrt(j*2)-0.6)*2; + GL11.glNormal3f(scale, 0.0F, 0.0F); + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + buffer.pos(-10 + j*4, -width, -width).tex((double)u3, (double)v3).endVertex(); + buffer.pos(-10 + j*4, -width, width).tex((double)u4, (double)v3).endVertex(); + buffer.pos(-10 + j*4, width, width).tex((double)u4, (double)v4).endVertex(); + buffer.pos(-10 + j*4, width, -width).tex((double)u3, (double)v4).endVertex(); + tessellator.draw(); + GL11.glNormal3f(-scale, 0.0F, 0.0F); + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + buffer.pos(-10 + j*4, width, -width).tex((double)u3, (double)v3).endVertex(); + buffer.pos(-10 + j*4, width, width).tex((double)u4, (double)v3).endVertex(); + buffer.pos(-10 + j*4, -width, width).tex((double)u4, (double)v4).endVertex(); + buffer.pos(-10 + j*4, -width, -width).tex((double)u3, (double)v4).endVertex(); + tessellator.draw(); + } + + GlStateManager.color(1, 1, 1, 1); + + for (int i = 0; i < 4; ++i) + { + GlStateManager.rotate(90.0F, 1.0F, 0.0F, 0.0F); + GL11.glNormal3f(0.0F, 0.0F, scale); + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + buffer.pos(-10.0D, -4.0D, 0.0D).tex((double)u1, (double)v1).endVertex(); + buffer.pos(10.0D, -4.0D, 0.0D).tex((double)u2, (double)v1).endVertex(); + buffer.pos(10.0D, 4.0D, 0.0D).tex((double)u2, (double)v2).endVertex(); + buffer.pos(-10.0D, 4.0D, 0.0D).tex((double)u1, (double)v2).endVertex(); + tessellator.draw(); + } + + GlStateManager.disableBlend(); + + GlStateManager.disableRescaleNormal(); + GlStateManager.enableLighting(); + GlStateManager.popMatrix(); + } + + @Override + protected ResourceLocation getEntityTexture(EntityForceArrow par1Entity) + { + return arrowTextures; + } +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderHammer.java b/src/main/java/electroblob/wizardry/client/renderer/RenderHammer.java new file mode 100644 index 00000000..b93fe9d2 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderHammer.java @@ -0,0 +1,39 @@ +package electroblob.wizardry.client.renderer; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.model.ModelHammer; +import electroblob.wizardry.entity.construct.EntityHammer; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.util.ResourceLocation; + +public class RenderHammer extends Render { + + private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_hammer.png"); + private ModelHammer model = new ModelHammer(); + + public RenderHammer(RenderManager renderManager){ + super(renderManager); + } + + @Override + public void doRender(EntityHammer entity, double x, double y, double z, float f, float f1) { + + GlStateManager.pushMatrix(); + GlStateManager.translate(x, y+1.5, z); + GlStateManager.rotate(180, 0F, 0F, 1F); + + this.bindTexture(texture); + + model.render(entity, 0, 0, 0, 0, 0, 0.0625f); + + GlStateManager.popMatrix(); + } + + @Override + protected ResourceLocation getEntityTexture(EntityHammer entity) { + return texture; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderIceGiant.java b/src/main/java/electroblob/wizardry/client/renderer/RenderIceGiant.java new file mode 100644 index 00000000..4ee0812e --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderIceGiant.java @@ -0,0 +1,39 @@ +package electroblob.wizardry.client.renderer; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.model.ModelIceGiant; +import electroblob.wizardry.entity.living.EntityIceGiant; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.entity.RenderLiving; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class RenderIceGiant extends RenderLiving { + + private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/ice_giant.png"); + + public RenderIceGiant(RenderManager renderManager){ + super(renderManager, new ModelIceGiant(), 0.5F); + } + + @Override + protected ResourceLocation getEntityTexture(EntityIceGiant entity){ + return texture; + } + + @Override + protected void rotateCorpse(EntityIceGiant entityLiving, float pitch, float yaw, float partialTicks){ + + super.rotateCorpse(entityLiving, pitch, yaw, partialTicks); + + if ((double)entityLiving.limbSwingAmount >= 0.01D){ + float f3 = 13.0F; + float f4 = entityLiving.limbSwing - entityLiving.limbSwingAmount * (1.0F - partialTicks) + 6.0F; + float f5 = (Math.abs(f4 % f3 - f3 * 0.5F) - f3 * 0.25F) / (f3 * 0.25F); + GlStateManager.rotate(6.5F * f5, 0.0F, 0.0F, 1.0F); + } + } +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderIceSpike.java b/src/main/java/electroblob/wizardry/client/renderer/RenderIceSpike.java new file mode 100644 index 00000000..0c4db9ef --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderIceSpike.java @@ -0,0 +1,84 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.construct.EntityIceSpike; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.ResourceLocation; + +public class RenderIceSpike extends Render { + + private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/ice_spike.png"); + + public RenderIceSpike(RenderManager renderManager){ + super(renderManager); + } + + @Override + public void doRender(EntityIceSpike entity, double x, double y, double z, float fa, float partialTickTime) { + + GlStateManager.pushMatrix(); + + GlStateManager.translate((float)x, (float)y, (float)z); + // Apparently, disabling lighting... doesn't disable lighting. Or at least, you can still set the brightness + // with setLightmapTextureCoords. + GlStateManager.disableLighting(); + + int j = entity.getBrightnessForRender(partialTickTime); + int k = j % 65536; + int l = j / 65536; + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)k / 1.0F, (float)l / 1.0F); + + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + + bindTexture(texture); + + // West face + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + buffer.pos(0, 0, 0.5).tex(1, 1).endVertex(); + buffer.pos(0, 1, 0.5).tex(1, 0).endVertex(); + buffer.pos(0, 1, -0.5).tex(0, 0).endVertex(); + buffer.pos(0, 0, -0.5).tex(0, 1).endVertex(); + tessellator.draw(); + // South face + buffer.begin(GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); + buffer.pos( 0.5, 0, 0).tex(1, 1).endVertex(); + buffer.pos( 0.5, 1, 0).tex(1, 0).endVertex(); + buffer.pos(-0.5, 1, 0).tex(0, 0).endVertex(); + buffer.pos(-0.5, 0, 0).tex(0, 1).endVertex(); + tessellator.draw(); + // East face + buffer.begin(GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); + buffer.pos(0, 0, -0.5).tex(0, 1).endVertex(); + buffer.pos(0, 1, -0.5).tex(0, 0).endVertex(); + buffer.pos(0, 1, 0.5).tex(1, 0).endVertex(); + buffer.pos(0, 0, 0.5).tex(1, 1).endVertex(); + tessellator.draw(); + // North face + buffer.begin(GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); + buffer.pos(-0.5, 0, 0).tex(0, 1).endVertex(); + buffer.pos(-0.5, 1, 0).tex(0, 0).endVertex(); + buffer.pos( 0.5, 1, 0).tex(1, 0).endVertex(); + buffer.pos( 0.5, 0, 0).tex(1, 1).endVertex(); + tessellator.draw(); + + GlStateManager.enableLighting(); + GlStateManager.popMatrix(); + } + + @Override + protected ResourceLocation getEntityTexture(EntityIceSpike entity) { + return texture; + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderLightningDisc.java b/src/main/java/electroblob/wizardry/client/renderer/RenderLightningDisc.java new file mode 100644 index 00000000..a9b620fa --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderLightningDisc.java @@ -0,0 +1,82 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.entity.projectile.EntityLightningDisc; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.ResourceLocation; + +public class RenderLightningDisc extends Render { + + private final ResourceLocation texture; + private float scale = 1.0f; + + public RenderLightningDisc(RenderManager renderManager, ResourceLocation texture, float scale) { + super(renderManager); + this.texture = texture; + this.scale = scale; + } + + @Override + public void doRender(EntityLightningDisc entity, double par2, double par4, double par6, float par8, float par9){ + + GlStateManager.pushMatrix(); + GlStateManager.enableBlend(); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + float yOffset = 0; + + GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6); + + this.bindTexture(texture); + float f6 = 1.0F; + float f7 = 0.5F; + float f8 = 0.5F; + + GlStateManager.rotate(-90, 1, 0, 0); + + GlStateManager.rotate(entity.ticksExisted*8, 0, 0, 1); + + GlStateManager.scale(scale, scale, scale); + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + //tessellator.setColorRGBA_I(k1, 128); + //buffer.normal(0.0F, 1.0F, 0.0F); + buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.01).tex(0, 1).endVertex(); + buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.01).tex(1, 1).endVertex(); + buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.01).tex(1, 0).endVertex(); + buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.01).tex(0, 0).endVertex(); + + tessellator.draw(); + + buffer.begin(GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); + //buffer.normal(0.0F, 1.0F, 0.0F); + buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.01).tex(0, 0).endVertex(); + buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.01).tex(1, 0).endVertex(); + buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.01).tex(1, 1).endVertex(); + buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.01).tex(0, 1).endVertex(); + + tessellator.draw(); + + GlStateManager.disableBlend(); + GlStateManager.enableLighting(); + GlStateManager.disableRescaleNormal(); + GlStateManager.popMatrix(); + } + + @Override + protected ResourceLocation getEntityTexture(EntityLightningDisc entity) { + return null; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderLightningPulse.java b/src/main/java/electroblob/wizardry/client/renderer/RenderLightningPulse.java new file mode 100644 index 00000000..2190011a --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderLightningPulse.java @@ -0,0 +1,72 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.construct.EntityLightningPulse; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.ResourceLocation; + +public class RenderLightningPulse extends Render { + + private final ResourceLocation[] textures = new ResourceLocation[8]; + private float scale = 1.0f; + + public RenderLightningPulse(RenderManager renderManager, float scale) { + super(renderManager); + for(int i=0; i { + + private final ResourceLocation texture; + private boolean blend; + private boolean renderEnds; + private double length = 8.0, width = 2.0; + private int pixelsLong = 16, pixelsWide = 5; + + public RenderMagicArrow(RenderManager renderManager, ResourceLocation texture, boolean blend, double length, double width, int pixelsLong, int pixelsWide, boolean renderEnds){ + super(renderManager); + this.texture = texture; + this.blend = blend; + this.renderEnds = renderEnds; + this.length = length; + this.width = width; + this.pixelsLong = pixelsLong; + this.pixelsWide = pixelsWide; + } + + @Override + public void doRender(EntityMagicArrow entity, double par2, double par4, double par6, float par8, float par9){ + + this.bindEntityTexture(entity); + GlStateManager.pushMatrix(); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + + if(this.blend){ + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + } + + GlStateManager.translate((float)par2, (float)par4, (float)par6); + GlStateManager.rotate(entity.prevRotationYaw + (entity.rotationYaw - entity.prevRotationYaw) * par9 - 90.0F, 0.0F, 1.0F, 0.0F); + GlStateManager.rotate(entity.prevRotationPitch + (entity.rotationPitch - entity.prevRotationPitch) * par9, 0.0F, 0.0F, 1.0F); + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + float f2 = 0.0F; + float f3 = pixelsLong / 32.0F; + float f4 = 0.0F; + float f5 = pixelsWide / 32.0F; + float f6 = 0.0F; + float f7 = 0.15625F; + float f8 = (float)5 / 32.0F; + float f9 = (float)10 / 32.0F; + float f10 = 0.05625F; + float f11 = 0.0f; + GlStateManager.enableRescaleNormal(); + //f11 = (float)par1EntityArrow.arrowShake - par9; + if (f11 > 0.0F) + { + float f12 = -MathHelper.sin(f11 * 3.0F) * f11; + GlStateManager.rotate(f12, 0.0F, 0.0F, 1.0F); + } + + GlStateManager.rotate(45.0F, 1.0F, 0.0F, 0.0F); + GlStateManager.scale(f10, f10, f10); + GlStateManager.translate(-4.0F, 0.0F, 0.0F); + GL11.glNormal3f(f10, 0.0F, 0.0F); + + if(renderEnds){ + // Ends + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + buffer.pos(-7.0D, -width, -width).tex((double)f6, (double)f8).endVertex(); + buffer.pos(-7.0D, -width, width).tex((double)f7, (double)f8).endVertex(); + buffer.pos(-7.0D, width, width).tex((double)f7, (double)f9).endVertex(); + buffer.pos(-7.0D, width, -width).tex((double)f6, (double)f9).endVertex(); + tessellator.draw(); + GL11.glNormal3f(-f10, 0.0F, 0.0F); + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + buffer.pos(-7.0D, width, -width).tex((double)f6, (double)f8).endVertex(); + buffer.pos(-7.0D, width, width).tex((double)f7, (double)f8).endVertex(); + buffer.pos(-7.0D, -width, width).tex((double)f7, (double)f9).endVertex(); + buffer.pos(-7.0D, -width, -width).tex((double)f6, (double)f9).endVertex(); + tessellator.draw(); + } + + for (int i = 0; i < 4; ++i){ + // Sides + GlStateManager.rotate(90.0F, 1.0F, 0.0F, 0.0F); + GL11.glNormal3f(0.0F, 0.0F, f10); + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + buffer.pos(-length, -width, 0.0D).tex((double)f2, (double)f4).endVertex(); + buffer.pos(length, -width, 0.0D).tex((double)f3, (double)f4).endVertex(); + buffer.pos(length, width, 0.0D).tex((double)f3, (double)f5).endVertex(); + buffer.pos(-length, width, 0.0D).tex((double)f2, (double)f5).endVertex(); + tessellator.draw(); + } + + if(this.blend){ + GlStateManager.disableBlend(); + } + + GlStateManager.disableRescaleNormal(); + GlStateManager.enableLighting(); + GlStateManager.popMatrix(); + } + + @Override + protected ResourceLocation getEntityTexture(EntityMagicArrow arrow) + { + return texture; + } +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderMagicLight.java b/src/main/java/electroblob/wizardry/client/renderer/RenderMagicLight.java new file mode 100644 index 00000000..1d49c546 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderMagicLight.java @@ -0,0 +1,130 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.tileentity.TileEntityMagicLight; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.ResourceLocation; + +public class RenderMagicLight extends TileEntitySpecialRenderer { + + private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/light_ray.png"); + private static final ResourceLocation texture2 = new ResourceLocation(Wizardry.MODID, "textures/entity/light_aura.png"); + + @Override + public void renderTileEntityAt(TileEntityMagicLight tileentity, double x, double y, double z, float f, int destroyStage){ + + GlStateManager.pushMatrix(); + + GlStateManager.disableCull(); + GlStateManager.enableBlend(); + GlStateManager.shadeModel(GL11.GL_SMOOTH); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + RenderHelper.disableStandardItemLighting(); + + GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5); + + if(tileentity.timer < 10){ + GlStateManager.scale((float)tileentity.timer/10, (float)tileentity.timer/10, (float)tileentity.timer/10); + } + if(tileentity.timer > tileentity.maxTimer-10){ + GlStateManager.scale((float)(tileentity.maxTimer-tileentity.timer)/10, (float)(tileentity.maxTimer-tileentity.timer)/10, (float)(tileentity.maxTimer-tileentity.timer)/10); + } + + // Renders the aura effect + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + + GlStateManager.pushMatrix(); + + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + // This counteracts the reverse rotation behaviour when in front f5 view. + // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. + float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2 ? Minecraft.getMinecraft().getRenderManager().playerViewX : -Minecraft.getMinecraft().getRenderManager().playerViewX; + GlStateManager.rotate(180.0F - Minecraft.getMinecraft().getRenderManager().playerViewY, 0.0F, 1.0F, 0.0F); + GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + this.bindTexture(texture2); + + buffer.pos(-0.6, 0.6, 0).tex(0, 0).endVertex(); + buffer.pos(0.6, 0.6, 0).tex(1, 0).endVertex(); + buffer.pos(0.6, -0.6, 0).tex(1, 1).endVertex(); + buffer.pos(-0.6, -0.6, 0).tex(0, 1).endVertex(); + + tessellator.draw(); + + GlStateManager.popMatrix(); + + // Renders the rays + + // For some reason, the old blend function (GL11.GL_SRC_ALPHA, GL11.GL_SRC_ALPHA) caused the innermost + // ends of the rays to appear black, so I have changed it to this, which looks very slightly different. + GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_SRC_ALPHA); + + this.bindTexture(texture); + + if(tileentity.randomiser.length >= 30){ + for(int j=0; j<30; j++){ + + int sliceAngle = 20 + tileentity.randomiser[j]; + float scale = 0.5f; + + GlStateManager.pushMatrix(); + + GlStateManager.rotate(31*tileentity.randomiser[j], 1, 0, 0); + GlStateManager.rotate(31*tileentity.randomiser2[j], 0, 0, 1); + + /* + * OK, so here are the changes to rendering as far as I know: + * Vertex formats specify how the methods are arranged + * Color has to be called for every vertex, I think. + * The new methods thing is a bit weird, because other than the number of arguments there is + * essentially no difference between pos, tex, color, normal and lightmap. At least they make + * the code more readable. + */ + + buffer.begin(5, DefaultVertexFormats.POSITION_TEX_COLOR); + + buffer.pos(0, 0, 0).tex(0, 0).color(255, 255, 255, 0).endVertex(); + buffer.pos(0, 0, 0).tex(0, 1).color(255, 255, 255, 0).endVertex(); + + double x1 = scale*Math.sin((tileentity.timer + 40*j)*(Math.PI/180)); + //double y1 = 0.7*Math.cos((timerentity.timer - 40*j)*(Math.PI/180))*j/10; + double z1 = scale*Math.cos((tileentity.timer + 40*j)*(Math.PI/180)); + + double x2 = scale*Math.sin((tileentity.timer + 40*j - sliceAngle)*(Math.PI/180)); + //double y2 = 0.7*Math.sin((timerentity.timer - 40*j)*(Math.PI/180))*j/10; + double z2 = scale*Math.cos((tileentity.timer + 40*j - sliceAngle)*(Math.PI/180)); + + buffer.pos(x1, 0, z1).tex(1, 0).color(0, 0, 0, 255).endVertex(); + buffer.pos(x2, 0, z2).tex(1, 1).color(0, 0, 0, 255).endVertex(); + + tessellator.draw(); + + GlStateManager.popMatrix(); + } + } + + GlStateManager.shadeModel(GL11.GL_FLAT); + GlStateManager.enableCull(); + GlStateManager.disableBlend(); + GlStateManager.enableLighting(); + RenderHelper.enableStandardItemLighting(); + + GlStateManager.popMatrix(); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderPhoenix.java b/src/main/java/electroblob/wizardry/client/renderer/RenderPhoenix.java new file mode 100644 index 00000000..bd070796 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderPhoenix.java @@ -0,0 +1,52 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.model.ModelPhoenix; +import electroblob.wizardry.entity.living.EntityPhoenix; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.entity.RenderLiving; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class RenderPhoenix extends RenderLiving { + + private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/phoenix.png"); + + public RenderPhoenix(RenderManager renderManager){ + super(renderManager, new ModelPhoenix(), 1.0f); + } + + @Override + protected ResourceLocation getEntityTexture(EntityPhoenix entity){ + return texture; + } + + @Override + protected void rotateCorpse(EntityPhoenix par1EntityPhoenix, float par2, float par3, float par4){ + GlStateManager.translate(0.0F, -0.1F, 0.0F); + super.rotateCorpse(par1EntityPhoenix, par2, par3, par4); + } + + @Override + public void doRender(EntityPhoenix phoenix, double par2, double par4, double par6, float par8, float par9){ + + GlStateManager.pushMatrix(); + + GlStateManager.disableLighting(); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + + super.doRender(phoenix, par2, par4, par6, par8, par9); + + GlStateManager.enableLighting(); + GlStateManager.disableBlend(); + GlStateManager.popMatrix(); + } +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderProjectile.java b/src/main/java/electroblob/wizardry/client/renderer/RenderProjectile.java new file mode 100644 index 00000000..dc00bf41 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderProjectile.java @@ -0,0 +1,88 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.entity.projectile.EntityMagicProjectile; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class RenderProjectile extends Render { + + private float scale; + private boolean blend = false; + + private final ResourceLocation texture; + + public RenderProjectile(RenderManager renderManager, float scale, ResourceLocation texture, boolean doBlending) + { + super(renderManager); + this.scale = scale; + this.texture = texture; + this.blend = doBlending; + } + + @Override + public void doRender(EntityMagicProjectile entity, double par2, double par4, double par6, float par8, float par9){ + + GlStateManager.pushMatrix(); + this.bindTexture(texture); + GlStateManager.translate((float)par2, (float)par4, (float)par6); + GlStateManager.enableRescaleNormal(); + if(blend){ + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + } + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + + float f2 = this.scale; + GlStateManager.scale(f2 / 1.0F, f2 / 1.0F, f2 / 1.0F); + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + + float f3 = 0.0f; + float f4 = 1.0f; + float f5 = 0.0f; + float f6 = 1.0f; + float f7 = 1.0F; + float f8 = 0.5F; + float f9 = 0.25F; + + // This counteracts the reverse rotation behaviour when in front f5 view. + // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. + float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2 ? this.renderManager.playerViewX : -this.renderManager.playerViewX; + GlStateManager.rotate(180.0F - this.renderManager.playerViewY, 0.0F, 1.0F, 0.0F); + GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + //buffer.normal(0.0F, 1.0F, 0.0F); + buffer.pos((double)(0.0F - f8), (double)(0.0F - f9), 0.0D).tex((double)f3, (double)f6).endVertex(); + buffer.pos((double)(f7 - f8), (double)(0.0F - f9), 0.0D).tex((double)f4, (double)f6).endVertex(); + buffer.pos((double)(f7 - f8), (double)(1.0F - f9), 0.0D).tex((double)f4, (double)f5).endVertex(); + buffer.pos((double)(0.0F - f8), (double)(1.0F - f9), 0.0D).tex((double)f3, (double)f5).endVertex(); + tessellator.draw(); + + GlStateManager.disableRescaleNormal(); + if(blend){ + GlStateManager.disableBlend(); + } + GlStateManager.enableLighting(); + GlStateManager.popMatrix(); + } + + @Override + protected ResourceLocation getEntityTexture(EntityMagicProjectile par1Entity){ + return texture; + } +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderSigil.java b/src/main/java/electroblob/wizardry/client/renderer/RenderSigil.java new file mode 100644 index 00000000..bb33b60d --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderSigil.java @@ -0,0 +1,89 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.entity.construct.EntityHealAura; +import electroblob.wizardry.entity.construct.EntityMagicConstruct; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +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.player.EntityPlayer; +import net.minecraft.util.ResourceLocation; + +public class RenderSigil extends Render { + + private final ResourceLocation texture; + private float scale = 1.0f; + private boolean invisibleToEnemies; + + public RenderSigil(RenderManager renderManager, ResourceLocation texture, float scale, boolean invisibleToEnemies) { + super(renderManager); + this.texture = texture; + this.scale = scale; + this.invisibleToEnemies = invisibleToEnemies; + } + + @Override + public void doRender(EntityMagicConstruct entity, double par2, double par4, double par6, float par8, float par9){ + + // Makes the sigil invisible to enemies of the player that created it + if(this.invisibleToEnemies){ + + if(entity.getCaster() instanceof EntityPlayer + && !WizardryUtilities.isPlayerAlly((EntityPlayer)entity.getCaster(), Minecraft.getMinecraft().thePlayer)){ + return; + } + } + + GlStateManager.pushMatrix(); + GlStateManager.enableBlend(); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + float yOffset = 0; + + GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6); + + this.bindTexture(texture); + float f6 = 1.0F; + float f7 = 0.5F; + float f8 = 0.5F; + + GlStateManager.rotate(-90, 1, 0, 0); + + // Healing aura rotates slowly + if(entity instanceof EntityHealAura) GlStateManager.rotate(entity.ticksExisted/3.0f, 0, 0, 1); + + GlStateManager.scale(scale, scale, scale); + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer buffer = tessellator.getBuffer(); + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + //tessellator.setColorRGBA_I(k1, 128); + //buffer.normal(0.0F, 1.0F, 0.0F); + buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.01).tex(0, 1).endVertex(); + buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.01).tex(1, 1).endVertex(); + buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.01).tex(1, 0).endVertex(); + buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.01).tex(0, 0).endVertex(); + + tessellator.draw(); + + GlStateManager.disableBlend(); + GlStateManager.enableLighting(); + GlStateManager.disableRescaleNormal(); + GlStateManager.popMatrix(); + } + + @Override + protected ResourceLocation getEntityTexture(EntityMagicConstruct entity) { + return null; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderSpiritHorse.java b/src/main/java/electroblob/wizardry/client/renderer/RenderSpiritHorse.java new file mode 100644 index 00000000..dda80293 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderSpiritHorse.java @@ -0,0 +1,36 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import net.minecraft.client.model.ModelHorse; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.entity.RenderHorse; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.entity.passive.EntityHorse; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class RenderSpiritHorse extends RenderHorse { + + private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/spirit_horse.png"); + + public RenderSpiritHorse(RenderManager renderManager, float par2){ + super(renderManager, new ModelHorse(), par2); + } + + @Override + protected ResourceLocation getEntityTexture(EntityHorse entity) { + return texture; + } + + @Override + protected void preRenderCallback(EntityHorse entitylivingbaseIn, float partialTickTime){ + super.preRenderCallback(entitylivingbaseIn, partialTickTime); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderSpiritWolf.java b/src/main/java/electroblob/wizardry/client/renderer/RenderSpiritWolf.java new file mode 100644 index 00000000..e1ffe797 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderSpiritWolf.java @@ -0,0 +1,35 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.Wizardry; +import net.minecraft.client.model.ModelWolf; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.entity.RenderWolf; +import net.minecraft.entity.passive.EntityWolf; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class RenderSpiritWolf extends RenderWolf { + + private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/spirit_wolf.png"); + + public RenderSpiritWolf(RenderManager renderManager, float par3){ + super(renderManager, new ModelWolf(), par3); + } + + @Override + protected ResourceLocation getEntityTexture(EntityWolf entity) { + return texture; + } + + @Override + protected void preRenderCallback(EntityWolf entity, float partialTickTime){ + super.preRenderCallback(entity, partialTickTime); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + } +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderStatue.java b/src/main/java/electroblob/wizardry/client/renderer/RenderStatue.java new file mode 100644 index 00000000..33301d84 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderStatue.java @@ -0,0 +1,64 @@ +package electroblob.wizardry.client.renderer; + +import electroblob.wizardry.tileentity.TileEntityStatue; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ResourceLocation; + +public class RenderStatue extends TileEntitySpecialRenderer { + + private int destroyStage = 0; // Gets set each time a statue is rendered to allow access from the layer renderer + + @Override + public void renderTileEntityAt(TileEntityStatue statue, double x, double y, double z, float partialTicks, int destroyStage){ + + // Multiblock support for the breaking animation. The chest has its own way of doing this in + // TileEntityRendererDispatcher, but I don't have access to that. + if(statue.position != 1 && destroyStage >= 0){ + TileEntity tileentity = statue.getWorld().getTileEntity(statue.getPos().down(statue.position-1)); + //System.out.println(tileentity); + if(tileentity instanceof TileEntityStatue){ + // If this is the block breaking animation pass and this isn't the bottom block, divert the call to + // the bottom block. + this.renderTileEntityAt((TileEntityStatue)tileentity, x, y - (statue.position-1), z, partialTicks, destroyStage); + } + } + + if(statue.creature != null && statue.position == 1){ + + this.destroyStage = destroyStage; + + GlStateManager.pushMatrix(); + // The next line makes stuff render in the same place relative to the world wherever the player is. + GlStateManager.translate((float)x + 0.5F, (float)y, (float)z + 0.5F); + GlStateManager.enableLighting(); + + float yaw = statue.creature.prevRotationYaw; + int i = statue.creature.getBrightnessForRender(0); + + int j = i % 65536; + int k = i / 65536; + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)j / 1.0F, (float)k / 1.0F); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + + GlStateManager.rotate(-yaw, 0F, 1F, 0F); + // Stops the normal model from rendering. + if(!statue.isIce) statue.creature.setInvisible(true); + // Setting the last parameter to true prevents the debug bounding box from rendering. + // For some reason, passing in the partialTicks causes the entity to spin round really fast + Minecraft.getMinecraft().getRenderManager().doRenderEntity(statue.creature, 0, 0, 0, 0, 0, true); + if(!statue.isIce) statue.creature.setInvisible(false); + + GlStateManager.popMatrix(); + + } + } + + public ResourceLocation getBlockBreakingTexture(){ + return destroyStage < 0 ? null : DESTROY_STAGES[destroyStage]; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderWizard.java b/src/main/java/electroblob/wizardry/client/renderer/RenderWizard.java new file mode 100644 index 00000000..e2043d5b --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderWizard.java @@ -0,0 +1,34 @@ +package electroblob.wizardry.client.renderer; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.model.ModelWizard; +import electroblob.wizardry.entity.living.EntityWizard; +import net.minecraft.client.renderer.entity.RenderBiped; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.entity.layers.LayerBipedArmor; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class RenderWizard extends RenderBiped { + + static final ResourceLocation[] textures = new ResourceLocation[6]; + + public RenderWizard(RenderManager renderManager){ + + super(renderManager, new ModelWizard(), 0.5F); + + for(int i=0;i<6;i++){ + textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/wizard_" + i + ".png"); + } + // Just using the default without overriding models, since the armour sets its own model anyway. + this.addLayer(new LayerBipedArmor(this)); + } + + @Override + protected ResourceLocation getEntityTexture(EntityWizard wizard) { + return textures[wizard.textureIndex]; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderWraithMinion.java b/src/main/java/electroblob/wizardry/client/renderer/RenderWraithMinion.java new file mode 100644 index 00000000..2c31a7cb --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderWraithMinion.java @@ -0,0 +1,26 @@ +package electroblob.wizardry.client.renderer; + +import electroblob.wizardry.entity.living.EntityBlazeMinion; +import net.minecraft.client.model.ModelBlaze; +import net.minecraft.client.renderer.entity.RenderLiving; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class RenderWraithMinion extends RenderLiving +{ + private ResourceLocation texture = new ResourceLocation("textures/entity/blaze.png"); + + public RenderWraithMinion(RenderManager renderManagerIn) + { + super(renderManagerIn, new ModelBlaze(), 0.5F); + } + + @Override + protected ResourceLocation getEntityTexture(EntityBlazeMinion entity) + { + return texture; + } +} diff --git a/src/main/java/electroblob/wizardry/command/CommandCastSpell.java b/src/main/java/electroblob/wizardry/command/CommandCastSpell.java new file mode 100644 index 00000000..7685851b --- /dev/null +++ b/src/main/java/electroblob/wizardry/command/CommandCastSpell.java @@ -0,0 +1,183 @@ +package electroblob.wizardry.command; + +import java.util.List; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.packet.PacketCastSpell; +import electroblob.wizardry.packet.WizardryPacketHandler; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.command.CommandBase; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.command.NumberInvalidException; +import net.minecraft.command.PlayerNotFoundException; +import net.minecraft.command.WrongUsageException; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.JsonToNBT; +import net.minecraft.nbt.NBTException; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; + +public class CommandCastSpell extends CommandBase { + + @Override + public String getCommandName(){ + return Wizardry.settings.castCommandName; + } + + @Override + public int getRequiredPermissionLevel(){ + // I *think* it's something like 0 = everyone, 1 = moderator, 2 = op/admin, 3 = op/console... + return 2; + } + + @Override + public String getCommandUsage(ICommandSender p_71518_1_){ + // Not ideal, but the way this is implemented means I have no choice. Only used in the help command, so in there + // the custom command name will not display. + return "commands.wizardry:cast.usage"; + //return I18n.format("commands.wizardry:cast.usage", Wizardry.settings.castCommandName); + } + + @Override + public List getTabCompletionOptions(MinecraftServer server, ICommandSender sender, String[] arguments, BlockPos pos) { + switch(arguments.length){ + case 1: return getListOfStringsMatchingLastWord(arguments, Spell.getSpellNames()); + case 2: return getListOfStringsMatchingLastWord(arguments, server.getAllUsernames()); + } + return super.getTabCompletionOptions(server, sender, arguments, pos); + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] arguments) throws CommandException { + + if(arguments.length < 1){ + throw new WrongUsageException("commands.wizardry:cast.usage", Wizardry.settings.castCommandName); + }else{ + + int i=0; + + EntityPlayerMP entityplayermp = null; + + try{ + entityplayermp = getCommandSenderAsPlayer(sender); + }catch(PlayerNotFoundException exception){ + // Nothing here since the player specifying is done later, I just don't want it to throw an exception here. + } + + Spell spell = Spell.get(arguments[i++]); + + if(spell == null){ + throw new NumberInvalidException("commands.wizardry:cast.not_found", new Object[]{arguments[i-1]}); + } + + boolean castAsOtherPlayer = false; + + if(i < arguments.length){ + try{ + // If the second argument is a player and is not the player that gave the command, the spell is cast + // as the given player rather than the command sender, and there is a different chat readout. + EntityPlayerMP entityplayermp1 = getPlayer(server, sender, arguments[i++]); + if(entityplayermp != entityplayermp1){ + castAsOtherPlayer = true; + entityplayermp = entityplayermp1; + } + }catch(PlayerNotFoundException exception){ + // If no player was found, rather than give an error it simply assumes the player was unspecified. + i--; + } + } + + // If, after this point, the player is still null, the sender must be a command block or the console and the + // player must not have been specified, meaning an exception should be thrown. + if(entityplayermp == null) throw new PlayerNotFoundException("You must specify which player you wish to perform this action on."); + + SpellModifiers modifiers = new SpellModifiers(); + + if(i < arguments.length){ + + // Copied from CommandGive. Why it doesn't just use arguments[i] itself I don't know. + String nbt = getChatComponentFromNthArg(sender, arguments, i++).getUnformattedText(); + + try{ + modifiers = SpellModifiers.fromNBT(JsonToNBT.getTagFromJson(nbt)); + }catch(NBTException nbtexception){ + throw new CommandException("commands.wizardry:cast.tag_error", nbtexception.getMessage()); + } + + for(float multiplier : modifiers.getModifiers().values()){ + if(multiplier < 0){ + throw new NumberInvalidException("commands.generic.double.tooSmall", multiplier, 0); + }else if(multiplier > Wizardry.settings.maxSpellCommandMultiplier){ + throw new NumberInvalidException("commands.generic.double.tooBig", multiplier, Wizardry.settings.maxSpellCommandMultiplier); + } + } + + } + + if(spell.isContinuous){ + + WizardData properties = WizardData.get((EntityPlayer)entityplayermp); + + if(properties != null){ + if(properties.isCasting()){ + WizardData.get((EntityPlayer)entityplayermp).stopCastingContinuousSpell(); + }else{ + + WizardData.get((EntityPlayer)entityplayermp).startCastingContinuousSpell(spell, modifiers); + + if(castAsOtherPlayer){ + sender.addChatMessage(new TextComponentTranslation("commands.wizardry:cast.success_remote_continuous", spell.getNameForTranslationFormatted(), entityplayermp.getName())); + }else{ + sender.addChatMessage(new TextComponentTranslation("commands.wizardry:cast.success_continuous", spell.getNameForTranslationFormatted())); + } + } + + return; + } + + }else{ + + if(spell.cast(entityplayermp.worldObj, entityplayermp, EnumHand.MAIN_HAND, 0, modifiers)){ + + if(spell.doesSpellRequirePacket()){ + // Sends a packet to all players in dimension to tell them to spawn particles. + // Only sent if the spell succeeded, because if the spell failed, you wouldn't + // need to spawn any particles! + IMessage msg = new PacketCastSpell.Message(entityplayermp.getEntityId(), null, spell.id(), modifiers); + WizardryPacketHandler.net.sendToDimension(msg, entityplayermp.worldObj.provider.getDimension()); + } + + if(WizardData.get((EntityPlayer)entityplayermp) != null){ + // Added optimisation from 1.7.10: if the spell was already discovered, nothing happens. + if(WizardData.get((EntityPlayer)entityplayermp).discoverSpell(spell)){ + // If the spell didn't send a packet itself, the extended player needs to be synced so the + // spell discovery updates on the client. + if(!spell.doesSpellRequirePacket()) WizardData.get((EntityPlayer)entityplayermp).sync(); + } + } + + if(castAsOtherPlayer){ + sender.addChatMessage(new TextComponentTranslation("commands.wizardry:cast.success_remote", spell.getNameForTranslationFormatted(), entityplayermp.getName())); + }else{ + sender.addChatMessage(new TextComponentTranslation("commands.wizardry:cast.success", spell.getNameForTranslationFormatted())); + } + return; + } + } + + ITextComponent message = new TextComponentTranslation("commands.wizardry:cast.fail", spell.getNameForTranslationFormatted()); + message.getStyle().setColor(TextFormatting.RED); + sender.addChatMessage(message); + } + } + +} diff --git a/src/main/java/electroblob/wizardry/command/CommandDiscoverSpell.java b/src/main/java/electroblob/wizardry/command/CommandDiscoverSpell.java new file mode 100644 index 00000000..c2496614 --- /dev/null +++ b/src/main/java/electroblob/wizardry/command/CommandDiscoverSpell.java @@ -0,0 +1,130 @@ +package electroblob.wizardry.command; + +import java.util.List; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.spell.Spell; +import net.minecraft.command.CommandBase; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.command.NumberInvalidException; +import net.minecraft.command.PlayerNotFoundException; +import net.minecraft.command.WrongUsageException; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextComponentTranslation; + +public class CommandDiscoverSpell extends CommandBase { + + @Override + public String getCommandName(){ + return Wizardry.settings.discoverspellCommandName; + } + + @Override + public int getRequiredPermissionLevel(){ + // I *think* it's something like 0 = everyone, 1 = moderator, 2 = op/admin, 3 = op/console... + return 2; + } + + /* + @Override + public boolean checkPermission(MinecraftServer server, ICommandSender sender){ + // Only ops (multiplayer) or players with cheats enabled (singleplayer/LAN) can use /discoverspell. + return !(sender instanceof EntityPlayer) || server.getServer().getConfigurationManager().func_152596_g(((EntityPlayer)sender).getGameProfile()); + } + */ + + @Override + public String getCommandUsage(ICommandSender p_71518_1_){ + // Not ideal, but the way this is implemented means I have no choice. Only used in the help command, so in there + // the custom command name will not display. + return "commands.wizardry:discoverspell.usage"; + //return I18n.format("commands.wizardry:discoverspell.usage", Wizardry.settings.discoverspellCommandName); + } + + @Override + public List getTabCompletionOptions(MinecraftServer server, ICommandSender sender, String[] arguments, BlockPos pos) { + switch(arguments.length){ + case 1: return getListOfStringsMatchingLastWord(arguments, Spell.getSpellNames()); + case 2: return getListOfStringsMatchingLastWord(arguments, server.getAllUsernames()); + } + return super.getTabCompletionOptions(server, sender, arguments, pos); + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] arguments) throws CommandException { + + if(arguments.length < 1){ + throw new WrongUsageException("commands.wizardry:discoverspell.usage", Wizardry.settings.discoverspellCommandName); + }else{ + + int i=0; + boolean clear = false; + boolean all = false; + + EntityPlayerMP entityplayermp = null; + + try{ + entityplayermp = getCommandSenderAsPlayer(sender); + }catch(PlayerNotFoundException exception){ + // Nothing here since the player specifying is done later, I just don't want it to throw an exception here. + } + + Spell spell = Spells.none; + + if(arguments[i].equals("clear")){ + clear = true; + i++; + }else if(arguments[i].equals("all")){ + all = true; + i++; + }else{ + + spell = Spell.get(arguments[i++]); + + if(spell == null){ + throw new NumberInvalidException("commands.wizardry:discoverspell.not_found", new Object[]{arguments[i-1]}); + } + } + + if(i < arguments.length){ + // If the second argument is a player and is not the player that gave the command, the spell is + // discovered as the given player rather than the command sender. + EntityPlayerMP entityplayermp1 = getPlayer(server, sender, arguments[i++]); + if(entityplayermp != entityplayermp1){ + entityplayermp = entityplayermp1; + } + } + + // If, after this point, the player is still null, the sender must be a command block or the console and the + // player must not have been specified, meaning an exception should be thrown. + if(entityplayermp == null) throw new PlayerNotFoundException("You must specify which player you wish to perform this action on."); + + WizardData properties = WizardData.get(entityplayermp); + + if(properties != null){ + if(clear){ + properties.spellsDiscovered.clear(); + sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.clear", entityplayermp.getName())); + }else if(all){ + properties.spellsDiscovered.addAll(Spell.getSpells(Spell.allSpells)); + sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.all", entityplayermp.getName())); + }else{ + if(properties.hasSpellBeenDiscovered(spell)){ + properties.spellsDiscovered.remove(spell); + sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.removespell", spell.getNameForTranslationFormatted(), entityplayermp.getName())); + }else{ + properties.discoverSpell(spell); + sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.addspell", spell.getNameForTranslationFormatted(), entityplayermp.getName())); + } + } + properties.sync(); + } + } + } + +} diff --git a/src/main/java/electroblob/wizardry/command/CommandSetAlly.java b/src/main/java/electroblob/wizardry/command/CommandSetAlly.java new file mode 100644 index 00000000..b566306c --- /dev/null +++ b/src/main/java/electroblob/wizardry/command/CommandSetAlly.java @@ -0,0 +1,113 @@ +package electroblob.wizardry.command; + +import java.util.List; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.command.CommandBase; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.command.NumberInvalidException; +import net.minecraft.command.PlayerNotFoundException; +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.text.TextComponentTranslation; +import net.minecraft.util.text.TextFormatting; + +public class CommandSetAlly extends CommandBase { + + @Override + public String getCommandName(){ + return Wizardry.settings.allyCommandName; + } + + @Override + public int getRequiredPermissionLevel(){ + // I *think* it's something like 0 = everyone, 1 = moderator, 2 = op/admin, 3 = op/console... + return 0; + } + + @Override + public boolean checkPermission(MinecraftServer server, ICommandSender p_71519_1_) + { + return true; + } + + @Override + public String getCommandUsage(ICommandSender p_71518_1_){ + // Not ideal, but the way this is implemented means I have no choice. Only used in the help command, so in there + // the custom command name will not display. + return "commands.wizardry:ally.usage"; + //return I18n.format("commands.wizardry:ally.usage", Wizardry.settings.allyCommandName); + } + + @Override + public List getTabCompletionOptions(MinecraftServer server, ICommandSender sender, String[] arguments, BlockPos pos) { + switch(arguments.length){ + case 1: return getListOfStringsMatchingLastWord(arguments, server.getAllUsernames()); + case 2: return getListOfStringsMatchingLastWord(arguments, server.getAllUsernames()); + } + return super.getTabCompletionOptions(server, sender, arguments, pos); + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] arguments) throws CommandException { + + if(arguments.length < 1){ + throw new WrongUsageException("commands.wizardry:ally.usage", Wizardry.settings.allyCommandName); + }else{ + + EntityPlayerMP allyOf = null; + + try{ + allyOf = getCommandSenderAsPlayer(sender); + }catch(PlayerNotFoundException exception){ + // Nothing here since the player specifying is done later, I just don't want it to throw an exception here. + } + + boolean executeAsOtherPlayer = false; + + EntityPlayerMP ally = getPlayer(server, sender, arguments[0]); + // Don't want to catch the exception here, because the first player argument is always required. + + if(arguments.length > 1){ + + allyOf = getPlayer(server, sender, arguments[1]); + // Don't want to catch the exception here either, because there can be no other second argument. + + if(allyOf != sender && sender instanceof EntityPlayer && !WizardryUtilities.isPlayerOp((EntityPlayer)sender, server)){ + // Displays a chat message if a non-op tries to modify another player's allies. + TextComponentTranslation TextComponentTranslation2 = new TextComponentTranslation("commands.wizardry:ally.permission"); + TextComponentTranslation2.getStyle().setColor(TextFormatting.RED); + allyOf.addChatMessage(TextComponentTranslation2); + return; + } + + if(allyOf != sender) executeAsOtherPlayer = true; + } + + // If, after this point, allyOf is still null, the sender must be a command block or the console and two + // players must not have been specified, meaning an exception should be thrown. + if(allyOf == null) throw new PlayerNotFoundException("You must specify which player you wish to perform this action on."); + + if(allyOf == ally) throw new NumberInvalidException("commands.wizardry:ally.self"); + + if(WizardData.get(allyOf) != null){ + String string = WizardData.get(allyOf).toggleAlly(ally) ? "add" : "remove"; + if(executeAsOtherPlayer){ + sender.addChatMessage(new TextComponentTranslation("commands.wizardry:ally." + string + "ally", ally.getName(), allyOf.getName())); + // In this case, the player whose allies have been modified is also notified. + allyOf.addChatMessage(new TextComponentTranslation("item.wand." + string + "ally", ally.getName())); + }else{ + sender.addChatMessage(new TextComponentTranslation("item.wand." + string + "ally", ally.getName())); + } + } + + } + } + +} diff --git a/src/main/java/electroblob/wizardry/command/CommandViewAllies.java b/src/main/java/electroblob/wizardry/command/CommandViewAllies.java new file mode 100644 index 00000000..257b4b85 --- /dev/null +++ b/src/main/java/electroblob/wizardry/command/CommandViewAllies.java @@ -0,0 +1,112 @@ +package electroblob.wizardry.command; + +import java.util.List; +import java.util.Set; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.client.resources.I18n; +import net.minecraft.command.CommandBase; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.command.PlayerNotFoundException; +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.text.TextComponentTranslation; +import net.minecraft.util.text.TextFormatting; + +public class CommandViewAllies extends CommandBase { + + @Override + public String getCommandName(){ + return Wizardry.settings.alliesCommandName; + } + + @Override + public int getRequiredPermissionLevel(){ + // I *think* it's something like 0 = everyone, 1 = moderator, 2 = op/admin, 3 = op/console... + return 0; + } + + @Override + public boolean checkPermission(MinecraftServer server, ICommandSender p_71519_1_) + { + return true; + } + + @Override + public String getCommandUsage(ICommandSender p_71518_1_){ + // Not ideal, but the way this is implemented means I have no choice. Only used in the help command, so in there + // the custom command name will not display. + return "commands.wizardry:allies.usage"; + //return I18n.format("commands.wizardry:allies.usage", Wizardry.settings.alliesCommandName); + } + + @Override + public List getTabCompletionOptions(MinecraftServer server, ICommandSender sender, String[] arguments, BlockPos pos) { + switch(arguments.length){ + case 1: return getListOfStringsMatchingLastWord(arguments, server.getAllUsernames()); + } + return super.getTabCompletionOptions(server, sender, arguments, pos); + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] arguments) throws CommandException { + + EntityPlayerMP player = null; + + try{ + player = getCommandSenderAsPlayer(sender); + }catch(PlayerNotFoundException exception){ + // Nothing here since the player specifying is done later, I just don't want it to throw an exception here. + } + + boolean executeAsOtherPlayer = false; + + if(arguments.length > 0){ + + player = getPlayer(server, sender, arguments[0]); + // Don't want to catch the exception here either, because there can be no other first argument. + + if(player != sender && sender instanceof EntityPlayer && !WizardryUtilities.isPlayerOp((EntityPlayer)sender, server)){ + // Displays a chat message if a non-op tries to view another player's allies. + TextComponentTranslation TextComponentTranslation2 = new TextComponentTranslation("commands.wizardry:allies.permission"); + TextComponentTranslation2.getStyle().setColor(TextFormatting.RED); + player.addChatMessage(TextComponentTranslation2); + return; + } + + if(player != sender) executeAsOtherPlayer = true; + } + + // If, after this point, player is still null, the sender must be a command block or the console and the + // player must not have been specified, meaning an exception should be thrown. + if(player == null) throw new PlayerNotFoundException("You must specify which player you wish to perform this action on."); + + if(WizardData.get(player) != null){ + + String string = ""; + Set names = WizardData.get(player).allyNames; + + if(!names.isEmpty()){ + for(String name : names){ + string = string + name + ", "; + } + // Cuts the last " ," off of the string. + string = string.substring(0, string.length() - 2); + }else{ + string = I18n.format("commands.wizardry:allies.none"); + } + + if(executeAsOtherPlayer){ + sender.addChatMessage(new TextComponentTranslation("commands.wizardry:allies.list_other", player.getName(), string)); + }else{ + sender.addChatMessage(new TextComponentTranslation("commands.wizardry:allies.list", string)); + } + } + } + +} diff --git a/src/main/java/electroblob/wizardry/constants/Constants.java b/src/main/java/electroblob/wizardry/constants/Constants.java new file mode 100644 index 00000000..95b79b9c --- /dev/null +++ b/src/main/java/electroblob/wizardry/constants/Constants.java @@ -0,0 +1,44 @@ +package electroblob.wizardry.constants; + +import electroblob.wizardry.WizardryEventHandler; + +/** Stores various global constants used in Wizardry. */ +public final class Constants { + + /** The amount of mana each magic crystal is worth */ + public static final int MANA_PER_CRYSTAL = 100; + /** The amount of mana each mana flask can hold */ + public static final int MANA_PER_FLASK = 700; + /** The maximum number of one type of wand upgrade which can be applied to a wand. */ + public static final int UPGRADE_STACK_LIMIT = 3; + /** The fraction by which cooldowns are reduced for each level of cooldown upgrade. */ + public static final float COOLDOWN_REDUCTION_PER_LEVEL = 0.15f; + /** The fraction by which maximum charge is increased for each level of storage upgrade. */ + public static final float STORAGE_INCREASE_PER_LEVEL = 0.15f; + /** The fraction by which damage is increased for each tier of matching wand. */ + public static final float DAMAGE_INCREASE_PER_TIER = 0.15f; + /** The fraction by which costs are reduced for each piece of matching armour. Note that changing this value will not + * affect continuous spells, since they are handled differently. */ + public static final float COST_REDUCTION_PER_ARMOUR = 0.2f; + /** The fraction by which spell duration is increased for each level of duration upgrade. */ + public static final float DURATION_INCREASE_PER_LEVEL = 0.25f; + /** The fraction by which spell range is increased for each level of range upgrade. */ + public static final float RANGE_INCREASE_PER_LEVEL = 0.25f; + /** The fraction by which spell blast radius is increased for each level of range upgrade. */ + public static final float BLAST_RADIUS_INCREASE_PER_LEVEL = 0.25f; + /** The fraction by which movement speed is reduced per level of frost effect. */ + public static final double FROST_SLOWNESS_PER_LEVEL = 0.5; + /** The fraction by which movement speed is reduced per level of decay effect. */ + public static final double DECAY_SLOWNESS_PER_LEVEL = 0.2; + /** The fraction by which dig speed is reduced per level of frost effect. */ + public static final float FROST_FATIGUE_PER_LEVEL = 0.45f; + /** The number of ticks between each mana increase for wands with the condenser upgrade. */ + public static final int CONDENSER_TICK_INTERVAL = 50; + /** The amount of mana given for a kill for each level of siphon upgrade. A random amount from 0 to this number - 1 + * is also added. See {@link WizardryEventHandler#onLivingDeathEvent} for more details. */ + public static final int SIPHON_MANA_PER_LEVEL = 3; + /** The number of ticks between the spawning of patches of decay when an entity has the decay effect. + * Note that decay won't spawn again if something is already standing in it. */ + public static final int DECAY_SPREAD_INTERVAL = 8; + +} diff --git a/src/main/java/electroblob/wizardry/constants/Element.java b/src/main/java/electroblob/wizardry/constants/Element.java new file mode 100644 index 00000000..48dd01f4 --- /dev/null +++ b/src/main/java/electroblob/wizardry/constants/Element.java @@ -0,0 +1,67 @@ +package electroblob.wizardry.constants; + +import electroblob.wizardry.Wizardry; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.Style; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public enum Element { + + /** The 'default' element, with {@link electroblob.wizardry.spell.MagicMissile MagicMissile} being its only spell. */ + MAGIC(new Style().setColor(TextFormatting.GRAY), "simple", Wizardry.MODID), + FIRE(new Style().setColor(TextFormatting.DARK_RED), "fire", Wizardry.MODID), + ICE(new Style().setColor(TextFormatting.AQUA), "ice", Wizardry.MODID), + LIGHTNING(new Style().setColor(TextFormatting.DARK_AQUA), "lightning", Wizardry.MODID), + NECROMANCY(new Style().setColor(TextFormatting.DARK_PURPLE), "necromancy", Wizardry.MODID), + EARTH(new Style().setColor(TextFormatting.DARK_GREEN), "earth", Wizardry.MODID), + SORCERY(new Style().setColor(TextFormatting.GREEN), "sorcery", Wizardry.MODID), + HEALING(new Style().setColor(TextFormatting.YELLOW), "healing", Wizardry.MODID); + + /** Display colour for this element */ + private final Style colour; + /** Unlocalised name for this element */ + private final String unlocalisedName; + /** The {@link ResourceLocation} for this element's 8x8 icon (displayed in the arcane workbench GUI) */ + private final ResourceLocation icon; + + private Element(Style colour, String name, String modid){ + this.colour = colour; + this.unlocalisedName = name; + this.icon = new ResourceLocation(modid, "textures/gui/element_icon_" + unlocalisedName + ".png"); + } + + /** Returns the translated display name of this element, without formatting. */ + @SideOnly(Side.CLIENT) + public String getDisplayName(){ + return net.minecraft.client.resources.I18n.format("element." + getUnlocalisedName()); + } + + /** Returns the {@link Style} object representing the colour of this element. */ + public Style getColour(){ + return colour; + } + + /** Returns the string formatting code which corresponds to the colour of this element. */ + public String getFormattingCode(){ + return colour.getFormattingCode(); + } + + /** Returns the translated display name for wizards of this element, shown in the trading GUI. */ + public ITextComponent getWizardName(){ + return new TextComponentTranslation("element." + getUnlocalisedName() + ".wizard"); + } + + /** Returns this element's unlocalised name. */ + public String getUnlocalisedName(){ + return unlocalisedName; + } + + /** Returns the {@link ResourceLocation} for this element's 8x8 icon (displayed in the arcane workbench GUI). */ + public ResourceLocation getIcon(){ + return icon; + } +} diff --git a/src/main/java/electroblob/wizardry/constants/SpellType.java b/src/main/java/electroblob/wizardry/constants/SpellType.java new file mode 100644 index 00000000..807d878c --- /dev/null +++ b/src/main/java/electroblob/wizardry/constants/SpellType.java @@ -0,0 +1,23 @@ +package electroblob.wizardry.constants; + +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public enum SpellType { + + ATTACK("attack"), + DEFENCE("defence"), + UTILITY("utility"), + MINION("minion"); + + private final String unlocalisedName; + + SpellType(String name){ + this.unlocalisedName = name; + } + + @SideOnly(Side.CLIENT) + public String getDisplayName(){ + return net.minecraft.client.resources.I18n.format("spelltype." + unlocalisedName); + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/constants/Tier.java b/src/main/java/electroblob/wizardry/constants/Tier.java new file mode 100644 index 00000000..f989b82b --- /dev/null +++ b/src/main/java/electroblob/wizardry/constants/Tier.java @@ -0,0 +1,81 @@ +package electroblob.wizardry.constants; + +import java.util.Random; + +import net.minecraft.util.text.Style; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public enum Tier { + + BASIC(700, 3, 12, new Style().setColor(TextFormatting.WHITE), "basic"), + APPRENTICE(1000, 4, 5, new Style().setColor(TextFormatting.AQUA), "apprentice"), + ADVANCED(1500, 5, 2, new Style().setColor(TextFormatting.DARK_BLUE), "advanced"), + MASTER(2500, 6, 1, new Style().setColor(TextFormatting.DARK_PURPLE), "master"); + + /** Maximum mana a wand of this tier can store. */ + public final int maxCharge; + /** Just an ordinal. Shouldn't really be needed but no point changing it now. */ + public final int level; + /** The maximum number of upgrades that can be applied to a wand of this tier. */ + public final int upgradeLimit; + /** The weight given to this tier in the standard weighting. */ + public final int weight; + /** The colour of text associated with this tier. */ + // Changed to a Style object for consistency. + private final Style colour; + + private final String unlocalisedName; + + private Tier(int maxCharge, int upgradeLimit, int weight, Style colour, String name){ + this.maxCharge = maxCharge; + this.level = ordinal(); + this.upgradeLimit = upgradeLimit; + this.weight = weight; + this.colour = colour; + this.unlocalisedName = name; + } + + @SideOnly(Side.CLIENT) + public String getDisplayName() { + return net.minecraft.client.resources.I18n.format("tier." + unlocalisedName); + } + + @SideOnly(Side.CLIENT) + public String getDisplayNameWithFormatting() { + return this.getFormattingCode() + net.minecraft.client.resources.I18n.format("tier." + unlocalisedName); + } + + public String getUnlocalisedName(){ + return unlocalisedName; + } + + public String getFormattingCode(){ + return colour.getFormattingCode(); + } + + /** Returns a random tier based on the standard weighting. Currently, the standard weighting is: Basic (Novice) 60%, + * Apprentice 25%, Advanced 10%, Master 5%. If an array of tiers is given, it picks a tier from the array, with the + * same relative weights for each. For example, if the array contains APPRENTICE and MASTER, then the weighting will + * become: Apprentice 83.3%, Master 16.7%. */ + public static Tier getWeightedRandomTier(Random random, Tier... tiers){ + + if(tiers.length == 0) tiers = values(); + + int totalWeight = 0; + + for(Tier tier : tiers) totalWeight += tier.weight; + + int randomiser = random.nextInt(totalWeight); + int cumulativeWeight = 0; + + for(Tier tier : tiers){ + cumulativeWeight += tier.weight; + if(randomiser < cumulativeWeight) return tier; + } + + // This will never happen, but it might as well be a sensible result. + return tiers[tiers.length-1]; + } +} diff --git a/src/main/java/electroblob/wizardry/enchantment/EnchantmentMagicSword.java b/src/main/java/electroblob/wizardry/enchantment/EnchantmentMagicSword.java new file mode 100644 index 00000000..0aca94ba --- /dev/null +++ b/src/main/java/electroblob/wizardry/enchantment/EnchantmentMagicSword.java @@ -0,0 +1,59 @@ +package electroblob.wizardry.enchantment; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentDamage; +import net.minecraft.entity.EnumCreatureAttribute; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.ItemStack; + +// This one is for imbued swords. The only reason this is separate is that the way vanilla is written allows me to hook +// into the damage increase for melee weapons, meaning I don't have to use events - always handy! +public class EnchantmentMagicSword extends EnchantmentDamage implements Imbuement { + + public EnchantmentMagicSword() { + super(Enchantment.Rarity.COMMON, 0, EntityEquipmentSlot.MAINHAND); + // Setting this to null stops the book appearing in the creative inventory + this.type = null; + } + + @Override + public boolean canApply(ItemStack p_92089_1_){ + return false; + } + + /** + * Returns the maximum level that the enchantment can have. + */ + // Here, enchantment level is the damage multiplier of the spell used to apply the enchantment, i.e. with an + // non-sorcerer wand it is level 1, a basic sorcerer wand is level 2, and so on. Note that basic sorcerer wands can't + // cast the imbue weapon spell, so level 2 is actually for apprentice wands. + @Override + public int getMaxLevel() + { + return 4; + } + + // Returns the number by which the damage should be increased (or something) + @Override + public float calcDamageByCreature(int p_152376_1_, EnumCreatureAttribute p_152376_2_) + { + return (float)p_152376_1_ * 1.25F; + } + + @Override + public String getName() + { + return "enchantment." + this.getRegistryName(); + } + + @Override + public boolean isAllowedOnBooks(){ + return false; + } + + @Override + public boolean canApplyAtEnchantingTable(ItemStack stack) { + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/enchantment/EnchantmentTimed.java b/src/main/java/electroblob/wizardry/enchantment/EnchantmentTimed.java new file mode 100644 index 00000000..d30252d1 --- /dev/null +++ b/src/main/java/electroblob/wizardry/enchantment/EnchantmentTimed.java @@ -0,0 +1,48 @@ +package electroblob.wizardry.enchantment; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.ItemStack; + +// This one is for everything other than imbued swords. +public class EnchantmentTimed extends Enchantment implements Imbuement { + + public EnchantmentTimed() { + // Setting enchantment type to null stops the book appearing in the creative inventory + super(Enchantment.Rarity.COMMON, null, new EntityEquipmentSlot[]{EntityEquipmentSlot.MAINHAND}); + } + + @Override + public boolean canApply(ItemStack p_92089_1_){ + return false; + } + + @Override + public String getName() + { + return "enchantment." + this.getRegistryName(); + } + + /** + * Returns the maximum level that the enchantment can have. + */ + // Here, enchantment level is the damage multiplier of the spell used to apply the enchantment, i.e. with an + // non-sorcerer wand it is level 1, an apprentice sorcerer wand is level 2, and so on. Note that basic sorcerer wands can't + // cast the imbue weapon spell, so level 2 is actually for apprentice wands. + @Override + public int getMaxLevel() + { + return 4; + } + + @Override + public boolean isAllowedOnBooks(){ + return false; + } + + @Override + public boolean canApplyAtEnchantingTable(ItemStack stack) { + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/enchantment/Imbuement.java b/src/main/java/electroblob/wizardry/enchantment/Imbuement.java new file mode 100644 index 00000000..4acf7f95 --- /dev/null +++ b/src/main/java/electroblob/wizardry/enchantment/Imbuement.java @@ -0,0 +1,11 @@ +package electroblob.wizardry.enchantment; + +/** Interface for temporary enchantments that last for a certain duration ('imbuements'). This interface allows + * {@link EnchantmentMagicSword} and {@link EnchantmentTimed} to both be treated as instances of a + * single type, rather than having to deal with each of them separately, + * which would be inefficient and cumbersome (the former of those classes cannot extend the latter because they both + * need to extend different subclasses of {@link net.minecraft.enchantment.Enchantment}). + * @since Wizardry 1.2 */ +public interface Imbuement { + +} diff --git a/src/main/java/electroblob/wizardry/entity/EntityArc.java b/src/main/java/electroblob/wizardry/entity/EntityArc.java new file mode 100644 index 00000000..dc68f0eb --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/EntityArc.java @@ -0,0 +1,73 @@ +package electroblob.wizardry.entity; + +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.Entity; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; + +public class EntityArc extends Entity implements IEntityAdditionalSpawnData { + + public int textureIndex = 0; + public double x1, y1, z1, x2, y2, z2; + // The number of ticks the arc lasts for before disappearing + public int lifetime = 3; + public double offsetX, offsetZ; + + public EntityArc(World par1World) { + super(par1World); + textureIndex = this.rand.nextInt(16); + this.ignoreFrustumCheck = true; + } + + public void setEndpointCoords(double x1, double y1, double z1, double x2, double y2, double z2){ + this.x1 = x1; + this.y1 = y1; + this.z1 = z1; + this.x2 = x2; + this.y2 = y2; + this.z2 = z2; + this.setPosition(x2, y2, z2); + } + + @Override + public void onUpdate(){ + if(this.ticksExisted >= lifetime){ + this.setDead(); + } + } + + protected void entityInit() + { + } + + @Override + protected void readEntityFromNBT(NBTTagCompound nbttagcompound) { + + } + + @Override + protected void writeEntityToNBT(NBTTagCompound nbttagcompound) { + // Nothing needed here; arc is merely a graphic effect that only exists for a few ticks; as such there is no need to save it. + } + + @Override + public boolean isInRangeToRenderDist(double distance) { + return true; + } + + @Override + public void writeSpawnData(ByteBuf data) { + data.writeDouble(this.x1); + data.writeDouble(this.y1); + data.writeDouble(this.z1); + } + + @Override + public void readSpawnData(ByteBuf data) { + this.x1 = data.readDouble(); + this.y1 = data.readDouble(); + this.z1 = data.readDouble(); + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/EntityMeteor.java b/src/main/java/electroblob/wizardry/entity/EntityMeteor.java new file mode 100644 index 00000000..fce647ce --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/EntityMeteor.java @@ -0,0 +1,127 @@ +package electroblob.wizardry.entity; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardryBlocks; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.item.EntityFallingBlock; +import net.minecraft.init.Blocks; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class EntityMeteor extends EntityFallingBlock { + + /** The entity blast multiplier. Only some projectiles cause a blast, which is why this isn't in EntityMagicProjectile. */ + public float blastMultiplier; + + public EntityMeteor(World world){ + super(world); + // Superconstructor doesn't call this. + this.setSize(0.98F, 0.98F); + } + + public EntityMeteor(World world, double x, double y, double z, float blastMultiplier){ + super(world, x, y, z, WizardryBlocks.meteor.getDefaultState()); + this.motionY = -1.0D; + this.setFire(200); + this.blastMultiplier = blastMultiplier; + } + + @Override + public double getYOffset() { + return this.height / 2.0F; + } + + @Override + public void onUpdate(){ + + if(this.ticksExisted % 16 == 1 && worldObj.isRemote){ + Wizardry.proxy.playMovingSound(this, WizardrySounds.SPELL_LOOP_FIRE, 3.0f, 1.0f, false); + } + + // You'd think the best way to do this would be to call super and do all the exploding stuff in fall() instead. + // However, for some reason, fallTile is null on the client side, causing an NPE in super.onUpdate() + + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + ++this.fallTime; + this.motionY -= 0.1d; //0.03999999910593033D; + this.moveEntity(this.motionX, this.motionY, this.motionZ); + this.motionX *= 0.9800000190734863D; + this.motionY *= 0.9800000190734863D; + this.motionZ *= 0.9800000190734863D; + + if(!this.worldObj.isRemote){ + + if(this.onGround){ + + this.motionX *= 0.699999988079071D; + this.motionZ *= 0.699999988079071D; + this.motionY *= -0.5D; + this.worldObj.createExplosion(this, this.posX, this.posY, this.posZ, 2.0f*blastMultiplier, true); + for(int i1=-3; i1<4; i1++){ + for(int j1=-3; j1<4; j1++){ + int y = WizardryUtilities.getNearestFloorLevelB(this.worldObj, new BlockPos(this.posX + i1, this.posY, this.posZ + j1), 7); + //System.out.println(y); + double dist = this.getDistance((int)this.posX + i1, y, (int)this.posZ + j1); + // Randomised with weighting so that the nearer the block the more likely it is to be set on fire. + if(y != -1 && rand.nextInt((int)dist*2 + 1) < 3 && dist < 4){ + this.worldObj.setBlockState(new BlockPos(this.posX + i1, y, this.posZ + j1), Blocks.FIRE.getDefaultState()); + } + } + } + this.setDead(); + } + } + } + + @Override + public void fall(float distance, float damageMultiplier){ + // Don't need to do anything here, the meteor should have already exploded. + } + + @SideOnly(Side.CLIENT) + @Override + public boolean canRenderOnFire(){ + return true; + } + + @Override + public IBlockState getBlock(){ + return WizardryBlocks.meteor.getDefaultState(); // For some reason the superclass version returns null on the client + } + + @SideOnly(Side.CLIENT) + @Override + public int getBrightnessForRender(float partialTicks){ + return 15728880; + } + + @Override + public float getBrightness(float partialTicks){ + return 1.0F; + } + + @Override + public boolean isInRangeToRenderDist(double distance){ + return true; + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + blastMultiplier = nbttagcompound.getFloat("blastMultiplier"); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + nbttagcompound.setFloat("blastMultiplier", blastMultiplier); + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/EntityShield.java b/src/main/java/electroblob/wizardry/entity/EntityShield.java new file mode 100644 index 00000000..c884ea07 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/EntityShield.java @@ -0,0 +1,94 @@ +package electroblob.wizardry.entity; + +import java.lang.ref.WeakReference; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.registry.WizardrySounds; +import net.minecraft.entity.Entity; +import net.minecraft.entity.IProjectile; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.DamageSource; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.world.World; + +public class EntityShield extends Entity { + + public WeakReference player; + + public EntityShield(World world){ + super(world); + this.noClip = true; + this.width = 1.2f; + this.height = 1.4f; + } + + public EntityShield(World par1World, EntityPlayer player) { + super(par1World); + this.width = 1.2f; + this.height = 1.4f; + this.player = new WeakReference(player); + this.noClip = true; + this.setPositionAndRotation(player.posX + player.getLookVec().xCoord, player.posY + 1 + player.getLookVec().yCoord, player.posZ + player.getLookVec().zCoord, player.rotationYawHead, player.rotationPitch); + this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 0.6f, this.posY - 0.7f, this.posZ - 0.6f, this.posX + 0.6f, this.posY + 0.7f, this.posZ + 0.6f)); + } + + @Override + public void onUpdate(){ + //System.out.println("Shield exists, ID: " + this.getUniqueID().toString()); + EntityPlayer entityplayer = player != null ? player.get() : null; + if(entityplayer != null){ + this.setPositionAndRotation(entityplayer.posX + entityplayer.getLookVec().xCoord*0.3, entityplayer.posY + 1 + entityplayer.getLookVec().yCoord*0.3, entityplayer.posZ + entityplayer.getLookVec().zCoord*0.3, entityplayer.rotationYawHead, entityplayer.rotationPitch); + if(!entityplayer.isHandActive() || entityplayer.getHeldItem(entityplayer.getActiveHand()) == null || !(entityplayer.getHeldItem(entityplayer.getActiveHand()).getItem() instanceof ItemWand)){ + WizardData.get(entityplayer).shield = null; + this.setDead(); + } + }else if(!worldObj.isRemote){ + this.setDead(); + } + } + + // Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow + // it to stick in blocks. + public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9) + { + this.setPosition(par1, par3, par5); + this.setRotation(par7, par8); + } + + public boolean attackEntityFrom(DamageSource par1DamageSource, float par2) + { + if(par1DamageSource != null && par1DamageSource.getSourceOfDamage() instanceof IProjectile){ + par1DamageSource.getSourceOfDamage().playSound(WizardrySounds.SPELL_DEFLECTION, 0.3f, 1.3f); + } + super.attackEntityFrom(par1DamageSource, par2); + return false; + } + + public boolean canBeCollidedWith() + { + return !this.isDead; + } + + public AxisAlignedBB getCollisionBox(Entity par1Entity) + { + return par1Entity.getEntityBoundingBox(); + } + + @Override + protected void entityInit() { + + } + + @Override + protected void readEntityFromNBT(NBTTagCompound nbttagcompound) { + + } + + @Override + protected void writeEntityToNBT(NBTTagCompound nbttagcompound) { + + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityArrowRain.java b/src/main/java/electroblob/wizardry/entity/construct/EntityArrowRain.java new file mode 100644 index 00000000..ddee05da --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityArrowRain.java @@ -0,0 +1,39 @@ +package electroblob.wizardry.entity.construct; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.projectile.EntityTippedArrow; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +public class EntityArrowRain extends EntityMagicConstruct { + + public EntityArrowRain(World par1World) { + super(par1World); + this.height = 3.0f; + this.width = 5.0f; + } + + public EntityArrowRain(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) { + super(world, x, y, z, caster, lifetime, damageMultiplier); + this.height = 3.0f; + this.width = 5.0f; + } + + public void onUpdate(){ + + super.onUpdate(); + + if(!this.worldObj.isRemote){ + EntityTippedArrow arrow = new EntityTippedArrow(worldObj, this.posX + rand.nextDouble()*6 - 3, this.posY + rand.nextDouble()*4 - 2, this.posZ + rand.nextDouble()*6 - 3); + arrow.motionX = Math.cos(Math.toRadians(this.rotationYaw + 90)); + arrow.motionY = -0.6; + arrow.motionZ = Math.sin(Math.toRadians(this.rotationYaw + 90)); + arrow.shootingEntity = this.getCaster(); + arrow.setDamage(7.0d*damageMultiplier); + arrow.setPotionEffect(new ItemStack(Items.ARROW)); + this.worldObj.spawnEntityInWorld(arrow); + } + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityBlackHole.java b/src/main/java/electroblob/wizardry/entity/construct/EntityBlackHole.java new file mode 100644 index 00000000..56f85762 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityBlackHole.java @@ -0,0 +1,138 @@ +package electroblob.wizardry.entity.construct; + +import java.util.List; + +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.SoundEvents; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.play.server.SPacketEntityVelocity; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class EntityBlackHole extends EntityMagicConstruct { + + public int[] randomiser; + public int[] randomiser2; + + public EntityBlackHole(World world){ + super(world); + this.width = 6.0f; + this.height = 3.0f; + randomiser = new int[30]; + for(int i=0; i targets = WizardryUtilities.getEntitiesWithinRadius(6.0d, this.posX, this.posY, this.posZ, this.worldObj); + + if(!this.worldObj.isRemote){ + + for(EntityLivingBase target : targets){ + + if(this.isValidTarget(target)){ + + // Sucks the target in + if(this.posX > target.posX && target.motionX < 1){ + target.motionX+=0.1; + }else if(this.posX < target.posX && target.motionX > -1){ + target.motionX-=0.1; + } + + if(this.posY > target.posY && target.motionY < 1){ + target.motionY+=0.1; + }else if(this.posY < target.posY && target.motionY > -1){ + target.motionY-=0.1; + } + + if(this.posZ > target.posZ && target.motionZ < 1){ + target.motionZ+=0.1; + }else if(this.posZ < target.posZ && target.motionZ > -1){ + target.motionZ-=0.1; + } + + // Player motion is handled on that player's client so needs packets + if(target instanceof EntityPlayerMP){ + ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + } + + if(this.getDistanceToEntity(target) <= 2){ + // Damages the target if it is close enough + if(this.getCaster() != null){ + target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC), 2*damageMultiplier); + }else{ + target.attackEntityFrom(DamageSource.magic, 2*damageMultiplier); + } + } + } + } + } + } + + /** + * Checks using a Vec3dd to determine if this entity is within range of that vector to be rendered. Args: Vec3dD + */ + public boolean isInRangeToRenderVec3dD(Vec3d par1Vec3d) + { + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityBlizzard.java b/src/main/java/electroblob/wizardry/entity/construct/EntityBlizzard.java new file mode 100644 index 00000000..342ad185 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityBlizzard.java @@ -0,0 +1,68 @@ +package electroblob.wizardry.entity.construct; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.DamageSource; +import net.minecraft.world.World; + +public class EntityBlizzard extends EntityMagicConstruct { + + public EntityBlizzard(World par1World) { + super(par1World); + this.height = 1.0f; + this.width = 1.0f; + } + + public EntityBlizzard(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) { + super(world, x, y, z, caster, lifetime, damageMultiplier); + this.height = 1.0f; + this.width = 1.0f; + } + + public void onUpdate(){ + + if(this.ticksExisted % 120 == 1){ + this.playSound(WizardrySounds.SPELL_LOOP_WIND, 1.0f, 1.0f); + } + + super.onUpdate(); + + if(!this.worldObj.isRemote){ + + List targets = WizardryUtilities.getEntitiesWithinRadius(3.0d, this.posX, this.posY, this.posZ, this.worldObj); + + for(EntityLivingBase target : targets){ + + if(this.isValidTarget(target)){ + + if(this.getCaster() != null){ + WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.FROST), 1*damageMultiplier); + }else{ + WizardryUtilities.attackEntityWithoutKnockback(target, DamageSource.magic, 1*damageMultiplier); + } + } + + // All entities are slowed, even the caster (except those immune to frost effects) + if(!MagicDamage.isEntityImmune(DamageType.FROST, target)) + target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 20, 0)); + } + }else{ + // For some reason this number of particles now causes the game to lag significantly, despite it being fine + // in 1.7.10. I thought particles were supposed to be LESS laggy now... + for(int i=1; i<6; i++){ + float brightness = 0.5f + (rand.nextFloat()/2); + Wizardry.proxy.spawnParticle(WizardryParticleType.BLIZZARD, worldObj, this.posX, this.posY + rand.nextDouble()*3, this.posZ, 0, 0, 0, 100, brightness, brightness + 0.1f, 1.0f, false, rand.nextDouble() * 2.5d + 0.5d); + Wizardry.proxy.spawnParticle(WizardryParticleType.BLIZZARD, worldObj, this.posX, this.posY + rand.nextDouble()*3, this.posZ, 0, 0, 0, 100, 1.0f, 1.0f, 1.0f, false, rand.nextDouble() * 2.5d + 0.5d); + } + } + } +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityBubble.java b/src/main/java/electroblob/wizardry/entity/construct/EntityBubble.java new file mode 100644 index 00000000..5ac8b2a7 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityBubble.java @@ -0,0 +1,123 @@ +package electroblob.wizardry.entity.construct; + +import java.lang.ref.WeakReference; + +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryUtilities; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.SoundEvents; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.world.World; + +public class EntityBubble extends EntityMagicConstruct { + + public boolean isDarkOrb; + + private WeakReference rider; + + public EntityBubble(World world){ + super(world); + } + + public EntityBubble(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, boolean isDarkOrb, float damageMultiplier) { + super(world, x, y, z, caster, lifetime, damageMultiplier); + //this.setSize(0.1f, 0.1f); + this.isDarkOrb = isDarkOrb; + } + + @Override + public double getMountedYOffset() + { + return 0.1; + } + + @Override + public boolean shouldRiderSit(){ + return false; + } + + public void onUpdate(){ + + super.onUpdate(); + + // Synchronises the rider field + if((this.rider == null || this.rider.get() == null) && WizardryUtilities.getRider(this) instanceof EntityLivingBase + && !WizardryUtilities.getRider(this).isDead){ + this.rider = new WeakReference((EntityLivingBase) WizardryUtilities.getRider(this)); + } + + // Prevents dismounting + if(WizardryUtilities.getRider(this) == null && this.rider != null && this.rider.get() != null && !this.rider.get().isDead){ + this.rider.get().startRiding(this); + } + + // Stops the bubble bursting instantly. + if(this.ticksExisted < 1 && !isDarkOrb) ((EntityLivingBase)WizardryUtilities.getRider(this)).hurtTime = 0; + + this.moveEntity(0, 0.03, 0); + + if(isDarkOrb){ + + if(WizardryUtilities.getRider(this) != null && this.ticksExisted % 30 == 0){ + if(this.getCaster() != null){ + WizardryUtilities.getRider(this).attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC), 1*damageMultiplier); + }else{ + WizardryUtilities.getRider(this).attackEntityFrom(DamageSource.magic, 1*damageMultiplier); + } + } + + for(int i=0; i<5; i++){ + this.worldObj.spawnParticle(EnumParticleTypes.PORTAL, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height + 0.5d, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, (this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(), (this.rand.nextDouble() - 0.5D) * 2.0D); + } + if(lifetime - this.ticksExisted == 75){ + this.playSound(SoundEvents.BLOCK_PORTAL_TRIGGER, 1.5f, 1.0f); + }else if(this.ticksExisted % 100 == 1 && this.ticksExisted < 150){ + this.playSound(SoundEvents.BLOCK_PORTAL_AMBIENT, 1.5f, 1.0f); + } + } + + // Bubble bursts if the entity is hurt (see event handler) or killed, or if the bubble has existed for more than 10 seconds. + if(WizardryUtilities.getRider(this) == null && this.ticksExisted > 1){ + if(!this.isDarkOrb) this.playSound(SoundEvents.ENTITY_ITEM_PICKUP, 1.5f, 1.0f); + this.setDead(); + } + } + + @Override + public void despawn(){ + if(WizardryUtilities.getRider(this) != null){ + ((EntityLivingBase)WizardryUtilities.getRider(this)).dismountEntity(this); + } + if(!this.isDarkOrb) this.playSound(SoundEvents.ENTITY_ITEM_PICKUP, 1.5f, 1.0f); + super.despawn(); + } + + @Override + protected void readEntityFromNBT(NBTTagCompound nbttagcompound) { + super.readEntityFromNBT(nbttagcompound); + isDarkOrb = nbttagcompound.getBoolean("isDarkOrb"); + } + + @Override + protected void writeEntityToNBT(NBTTagCompound nbttagcompound) { + super.writeEntityToNBT(nbttagcompound); + nbttagcompound.setBoolean("isDarkOrb", isDarkOrb); + } + + @Override + public void writeSpawnData(ByteBuf data) { + super.writeSpawnData(data); + data.writeBoolean(this.isDarkOrb); + } + + @Override + public void readSpawnData(ByteBuf data) { + super.readSpawnData(data); + this.isDarkOrb = data.readBoolean(); + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityDecay.java b/src/main/java/electroblob/wizardry/entity/construct/EntityDecay.java new file mode 100644 index 00000000..f8d33151 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityDecay.java @@ -0,0 +1,78 @@ +package electroblob.wizardry.entity.construct; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.SoundEvents; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class EntityDecay extends EntityMagicConstruct { + + public int textureIndex = 0; + public static final int LIFETIME = 400; + + public EntityDecay(World par1World) { + super(par1World); + textureIndex = this.rand.nextInt(10); + this.height = 0.2f; + this.width = 2.0f; + } + + public EntityDecay(World par1World, double x, double y, double z, EntityLivingBase caster) { + super(par1World, x, y, z, caster, LIFETIME, 1); + textureIndex = this.rand.nextInt(10); + this.height = 0.2f; + this.width = 2.0f; + } + + @Override + public void onUpdate(){ + + super.onUpdate(); + + if(this.rand.nextInt(700) == 0 && this.ticksExisted+100 < LIFETIME) this.playSound(SoundEvents.BLOCK_LAVA_AMBIENT, 0.2F + rand.nextFloat() * 0.2F, 0.6F + rand.nextFloat() * 0.15F); + + if(!this.worldObj.isRemote){ + List targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY, this.posZ, this.worldObj); + for(EntityLivingBase target : targets){ + if(target != this.getCaster()){ + // If this check wasn't here the potion would be reapplied every tick and hence the entity would be damaged each tick. + // In this case, we do want particles to be shown. + if(!target.isPotionActive(WizardryPotions.decay)) target.addPotionEffect(new PotionEffect(WizardryPotions.decay, LIFETIME, 0)); + } + } + }else if(this.rand.nextInt(15) == 0){ + double radius = rand.nextDouble()*0.8; + double angle = rand.nextDouble()*Math.PI*2; + float brightness = rand.nextFloat()*0.4f; + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + radius*Math.cos(angle), this.posY, this.posZ + radius*Math.sin(angle), 0, 0, 0, 0, brightness, 0, brightness+0.1f); + } + } + + protected void entityInit(){} + + @Override + protected void readEntityFromNBT(NBTTagCompound nbttagcompound) { + + } + + @Override + protected void writeEntityToNBT(NBTTagCompound nbttagcompound) { + + } + + /** + * Checks using a Vec3dd to determine if this entity is within range of that vector to be rendered. Args: Vec3dD + */ + public boolean isInRangeToRenderVec3dD(Vec3d par1Vec3d) + { + return true; + } +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityEarthquake.java b/src/main/java/electroblob/wizardry/entity/construct/EntityEarthquake.java new file mode 100644 index 00000000..10d32231 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityEarthquake.java @@ -0,0 +1,107 @@ +package electroblob.wizardry.entity.construct; + +import java.util.List; + +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.item.EntityFallingBlock; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.MobEffects; +import net.minecraft.network.play.server.SPacketEntityVelocity; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class EntityEarthquake extends EntityMagicConstruct { + + public EntityEarthquake(World world){ + super(world); + this.height = 1.0f; + this.width = 1.0f; + } + + public EntityEarthquake(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, + float damageMultiplier) { + super(world, x, y, z, caster, lifetime, damageMultiplier); + this.height = 1.0f; + this.width = 1.0f; + } + + public void onUpdate(){ + + super.onUpdate(); + + if(!worldObj.isRemote){ + + double speed = 0.4; + + // The further the earthquake is going to spread, the finer the angle increments. + for(double angle=0; angle < 2*Math.PI; angle+=Math.PI/(lifetime*1.5)){ + + // Calculates coordinates for the block to be moved. The radius increases with time. The +1.5 is to leave + // blocks in the centre untouched. + int x = this.posX < 0 ? (int)(this.posX + ((this.ticksExisted*speed)+1.5)*Math.sin(angle) - 1) : (int)(this.posX + ((this.ticksExisted*speed)+1.5)*Math.sin(angle)); + int y = (int)(this.posY - 0.5); + int z = this.posZ < 0 ? (int)(this.posZ + ((this.ticksExisted*speed)+1.5)*Math.cos(angle) - 1) : (int)(this.posZ + ((this.ticksExisted*speed)+1.5)*Math.cos(angle)); + + BlockPos pos = new BlockPos(x, y, z); + + if(!WizardryUtilities.isBlockUnbreakable(worldObj, pos) && !worldObj.isAirBlock(pos) && worldObj.isBlockNormalCube(pos, false) + // Checks that the block above is not solid, since this causes the falling sand to vanish. + && !worldObj.isBlockNormalCube(pos.up(), false)){ + + // Falling blocks do the setting block to air themselves. + EntityFallingBlock fallingblock = new EntityFallingBlock(worldObj, x+0.5, y+0.5, z+0.5, worldObj.getBlockState(new BlockPos(x, y, z))); + fallingblock.motionY = 0.3; + worldObj.spawnEntityInWorld(fallingblock); + } + } + + List targets = WizardryUtilities.getEntitiesWithinRadius((this.ticksExisted*speed)+1.5, this.posX, this.posY, this.posZ, worldObj); + + // In this particular instance, the caster is completely unaffected because they will always be in the centre. + targets.remove(this.getCaster()); + + for(EntityLivingBase target : targets){ + + // Searches in a 1 wide ring. + if(this.getDistanceToEntity(target) > (this.ticksExisted*speed)+0.5 && target.posY < this.posY + 1 && target.posY > this.posY - 1){ + + // Knockback must be removed in this instance, or the target will fall into the floor. + double motionX = target.motionX; + double motionZ = target.motionZ; + + if(this.isValidTarget(target)){ + target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.BLAST), 10*this.damageMultiplier); + target.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, 400, 1)); + } + + // All targets are thrown, even those immune to the damage, so they don't fall into the ground. + target.motionX = motionX; + target.motionY = 0.8; // Throws target into the air. + target.motionZ = motionZ; + + // Player motion is handled on that player's client so needs packets + if(target instanceof EntityPlayerMP){ + ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + } + } + } + // TODO: Uncomment once 2.1.0 is released +// }else{ +// +// // Constant 15 blocks for now +// List targets = WizardryUtilities.getEntitiesWithinRadius(15, this.posX, this.posY, this.posZ, worldObj, EntityPlayer.class); +// +// float magnitude = 6f * ((float)(this.lifetime - this.ticksExisted))/(float)this.lifetime; +// +// // Makes the screen shake +// for(EntityLivingBase target : targets){ +// target.setAngles(0, this.ticksExisted % 4 < 2 ? magnitude : -magnitude); +// } + } + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityFireRing.java b/src/main/java/electroblob/wizardry/entity/construct/EntityFireRing.java new file mode 100644 index 00000000..e2055da5 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityFireRing.java @@ -0,0 +1,67 @@ +package electroblob.wizardry.entity.construct; + +import java.util.List; + +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.SoundEvents; +import net.minecraft.util.DamageSource; +import net.minecraft.world.World; + +public class EntityFireRing extends EntityMagicConstruct { + + public EntityFireRing(World par1World) { + super(par1World); + this.height = 1.0f; + this.width = 5.0f; + } + + public EntityFireRing(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) { + super(world, x, y, z, caster, lifetime, damageMultiplier); + this.height = 1.0f; + this.width = 5.0f; + } + + public void onUpdate(){ + + if(this.ticksExisted % 40 == 1){ + this.playSound(SoundEvents.BLOCK_FIRE_AMBIENT, 4.0f, 0.7f); + } + + super.onUpdate(); + + if(!this.worldObj.isRemote){ + + List targets = WizardryUtilities.getEntitiesWithinRadius(2.5d, this.posX, this.posY, this.posZ, this.worldObj); + + for(EntityLivingBase target : targets){ + + if(this.isValidTarget(target)){ + + double velX = target.motionX; + double velY = target.motionY; + double velZ = target.motionZ; + + if(!MagicDamage.isEntityImmune(DamageType.FIRE, target)){ + + target.setFire(10); + + if(this.getCaster() != null){ + target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.FIRE), 1*damageMultiplier); + }else{ + target.attackEntityFrom(DamageSource.magic, 1*damageMultiplier); + } + } + + // Removes knockback + target.motionX = velX; + target.motionY = velY; + target.motionZ = velZ; + } + } + } + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityFireSigil.java b/src/main/java/electroblob/wizardry/entity/construct/EntityFireSigil.java new file mode 100644 index 00000000..9913006e --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityFireSigil.java @@ -0,0 +1,87 @@ +package electroblob.wizardry.entity.construct; + +import java.util.List; + +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.SoundEvents; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.world.World; + +public class EntityFireSigil extends EntityMagicConstruct { + + public EntityFireSigil(World par1World) { + super(par1World); + this.height = 0.2f; + this.width = 2.0f; + } + + public EntityFireSigil(World par1World, double x, double y, double z, EntityLivingBase caster, float damageMultiplier) { + super(par1World, x, y, z, caster, -1, damageMultiplier); + this.height = 0.2f; + this.width = 2.0f; + } + + // Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow + // it to stick in blocks. + public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9) + { + this.setPosition(par1, par3, par5); + this.setRotation(par7, par8); + } + + public void onUpdate(){ + + super.onUpdate(); + + if(!this.worldObj.isRemote){ + + List targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY, this.posZ, this.worldObj); + + for(EntityLivingBase target : targets){ + + if(this.isValidTarget(target)){ + + double velX = target.motionX; + double velY = target.motionY; + double velZ = target.motionZ; + + target.attackEntityFrom(this.getCaster() != null ? MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.FIRE) : DamageSource.magic, 6); + + // Removes knockback + target.motionX = velX; + target.motionY = velY; + target.motionZ = velZ; + + if(!MagicDamage.isEntityImmune(DamageType.FIRE, target)) target.setFire(10); + + this.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1); + + // The trap is destroyed once triggered. + this.setDead(); + } + } + }else if(this.rand.nextInt(15) == 0){ + double radius = 0.5 + rand.nextDouble()*0.3; + double angle = rand.nextDouble()*Math.PI*2; + worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + radius*Math.cos(angle), this.posY + 0.1, this.posZ + radius*Math.sin(angle), 0, 0, 0); + } + } + + @Override + protected void entityInit() { + + } + + /** + * Return whether this entity should be rendered as on fire. + */ + public boolean canRenderOnFire() + { + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityForcefield.java b/src/main/java/electroblob/wizardry/entity/construct/EntityForcefield.java new file mode 100644 index 00000000..f0f817eb --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityForcefield.java @@ -0,0 +1,91 @@ +package electroblob.wizardry.entity.construct; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.play.server.SPacketEntityVelocity; +import net.minecraft.util.DamageSource; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.world.World; + +public class EntityForcefield extends EntityMagicConstruct { + + public EntityForcefield(World world) { + super(world); + this.height = 6; + this.width = 6; + this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 3, this.posY - 3, this.posZ - 3, this.posX + 3, this.posY + 3, this.posZ + 3)); + } + + public EntityForcefield(World world, double x, double y, double z, EntityLivingBase caster, int lifetime) { + // y-3 because it needs to be centred on the given position + // Damage multiplier is 1 because forcefields do no damage! + super(world, x, y-3, z, caster, lifetime, 1.0f); + this.height = 6; + this.width = 6; + this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 3, this.posY - 3, this.posZ - 3, this.posX + 3, this.posY + 3, this.posZ + 3)); + } + + public boolean canBeCollidedWith() + { + return !this.isDead; + } + + public AxisAlignedBB getCollisionBox(Entity par1Entity) + { + return par1Entity.getEntityBoundingBox(); + } + + public void onUpdate(){ + + super.onUpdate(); + + if(!this.worldObj.isRemote){ + List targets = WizardryUtilities.getEntitiesWithinRadius(3.5, this.posX, this.posY + 3, this.posZ, this.worldObj); + for(EntityLivingBase target : targets){ + if(this.isValidTarget(target)){ + double multiplier = (3.5 - target.getDistance(this.posX, this.posY+3, this.posZ))*0.1; + target.addVelocity((target.posX - this.posX)*multiplier, (target.posY - (this.posY + 3))*multiplier, (target.posZ - this.posZ)*multiplier); + // Player motion is handled on that player's client so needs packets + if(target instanceof EntityPlayerMP){ + ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + } + } + } + }else{ + for(int i=1; i<40; i++){ + float brightness = 0.5f + (rand.nextFloat()*0.5f); + double radius = 3; + double yaw = rand.nextDouble()*Math.PI*2; + double pitch = (rand.nextDouble()-0.5)*Math.PI; + // Generates a spherical pattern of particles + Wizardry.proxy.spawnParticle(WizardryParticleType.BRIGHT_DUST, worldObj, this.posX + radius*Math.cos(yaw)*Math.cos(pitch), this.posY + 3 + radius*Math.sin(pitch), this.posZ + radius*Math.sin(yaw)*Math.cos(pitch), 0, 0, 0, 48 + this.rand.nextInt(12), brightness, brightness, 1.0f); + } + } + } + + public boolean attackEntityFrom(DamageSource source, float par2){ + + if(source != null && source.getSourceOfDamage() != null){ + // Now works for any source of damage. + source.getSourceOfDamage().playSound(WizardrySounds.SPELL_DEFLECTION, 0.3f, 1.3f); + } + super.attackEntityFrom(source, par2); + return false; + } + + /** + * Return whether this entity should be rendered as on fire. + */ + public boolean canRenderOnFire() + { + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityFrostSigil.java b/src/main/java/electroblob/wizardry/entity/construct/EntityFrostSigil.java new file mode 100644 index 00000000..f2cb3ffe --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityFrostSigil.java @@ -0,0 +1,91 @@ +package electroblob.wizardry.entity.construct; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.DamageSource; +import net.minecraft.world.World; + +public class EntityFrostSigil extends EntityMagicConstruct { + + public EntityFrostSigil(World par1World) { + super(par1World); + this.height = 0.2f; + this.width = 2.0f; + } + + public EntityFrostSigil(World par1World, double x, double y, double z, EntityLivingBase caster, float damageMultiplier) { + super(par1World, x, y, z, caster, -1, damageMultiplier); + this.height = 0.2f; + this.width = 2.0f; + } + + // Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow + // it to stick in blocks. + public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9) + { + this.setPosition(par1, par3, par5); + this.setRotation(par7, par8); + } + + public void onUpdate(){ + + super.onUpdate(); + + if(!this.worldObj.isRemote){ + + List targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY, this.posZ, this.worldObj); + + for(EntityLivingBase target : targets){ + + if(this.isValidTarget(target)){ + + double velX = target.motionX; + double velY = target.motionY; + double velZ = target.motionZ; + + target.attackEntityFrom(this.getCaster() != null ? MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.FROST) : DamageSource.magic, 8); + + // Removes knockback + target.motionX = velX; + target.motionY = velY; + target.motionZ = velZ; + + if(!MagicDamage.isEntityImmune(DamageType.FROST, target)) + target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 200, 1)); + + this.playSound(WizardrySounds.SPELL_FREEZE, 1.0f, 1.0f); + + // The trap is destroyed once triggered. + this.setDead(); + } + } + }else if(this.rand.nextInt(15) == 0){ + double radius = 0.5 + rand.nextDouble()*0.3; + double angle = rand.nextDouble()*Math.PI*2; + Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, worldObj, this.posX + radius*Math.cos(angle), this.posY + 0.1, this.posZ + radius*Math.sin(angle), 0, 0, 0, 40 + rand.nextInt(10)); + } + } + + @Override + protected void entityInit() { + + } + + /** + * Return whether this entity should be rendered as on fire. + */ + public boolean canRenderOnFire() + { + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityHailstorm.java b/src/main/java/electroblob/wizardry/entity/construct/EntityHailstorm.java new file mode 100644 index 00000000..6b627bdd --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityHailstorm.java @@ -0,0 +1,37 @@ +package electroblob.wizardry.entity.construct; + +import electroblob.wizardry.entity.projectile.EntityIceShard; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.world.World; + +public class EntityHailstorm extends EntityMagicConstruct { + + public EntityHailstorm(World par1World) { + super(par1World); + this.height = 3.0f; + this.width = 5.0f; + } + + public EntityHailstorm(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) { + super(world, x, y, z, caster, lifetime, damageMultiplier); + this.height = 3.0f; + this.width = 5.0f; + } + + public void onUpdate(){ + + super.onUpdate(); + + if(!this.worldObj.isRemote){ + //System.out.println(this.rotationYaw); + EntityIceShard iceshard = new EntityIceShard(worldObj, this.posX + rand.nextDouble()*6 - 3, this.posY + rand.nextDouble()*4 - 2, this.posZ + rand.nextDouble()*6 - 3); + iceshard.motionX = Math.cos(Math.toRadians(this.rotationYaw + 90)); + iceshard.motionY = -0.6; + iceshard.motionZ = Math.sin(Math.toRadians(this.rotationYaw + 90)); + iceshard.setShootingEntity(this.getCaster()); + iceshard.damageMultiplier = this.damageMultiplier; + this.worldObj.spawnEntityInWorld(iceshard); + } + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityHammer.java b/src/main/java/electroblob/wizardry/entity/construct/EntityHammer.java new file mode 100644 index 00000000..67950d41 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityHammer.java @@ -0,0 +1,183 @@ +package electroblob.wizardry.entity.construct; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.EntityArc; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.effect.EntityLightningBolt; +import net.minecraft.init.SoundEvents; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class EntityHammer extends EntityMagicConstruct { + + /** How long the hammer has been falling for. */ + public int fallTime; + + public EntityHammer(World par1World){ + super(par1World); + this.setSize(1.0f, 1.9F); + this.noClip = false; + } + + public EntityHammer(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier){ + super(world, x, y, z, caster, lifetime, damageMultiplier); + this.setSize(1.0f, 1.9F); + this.motionX = 0.0D; + this.motionY = 0.0D; + this.motionZ = 0.0D; + this.noClip = false; + } + + @Override + public boolean isBurning(){ + return false; + } + + @Override + public boolean canBeCollidedWith(){ + return true; + } + + @Override + public AxisAlignedBB getCollisionBoundingBox(){ + return this.getEntityBoundingBox(); + } + + @Override + public void onUpdate(){ + + super.onUpdate(); + + if(this.ticksExisted % 20 == 1 && !this.onGround && worldObj.isRemote){ + // Though this sound does repeat, it stops when it hits the ground. + Wizardry.proxy.playMovingSound(this, WizardrySounds.SPELL_LOOP_LIGHTNING, 3.0f, 1.0f, false); + } + + if(this.worldObj.isRemote && this.ticksExisted % 3 == 0){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + 2*rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0, 0, 3); + } + + if(!this.worldObj.isRemote){ + + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + ++this.fallTime; + this.motionY -= 0.03999999910593033D; + this.moveEntity(this.motionX, this.motionY, this.motionZ); + this.motionX *= 0.9800000190734863D; + this.motionY *= 0.9800000190734863D; + this.motionZ *= 0.9800000190734863D; + + if(this.onGround){ + + this.motionX *= 0.699999988079071D; + this.motionZ *= 0.699999988079071D; + this.motionY *= -0.5D; + + if(this.ticksExisted % 40 == 0){ + + double seekerRange = 10.0d; + + List targets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, this.posX, this.posY+1, this.posZ, worldObj); + + // For this spell there is no limit to the amount of secondary targets! + for(EntityLivingBase target : targets){ + + if(this.isValidTarget(target)){ + + if(!worldObj.isRemote){ + EntityArc arc = new EntityArc(worldObj); + arc.setEndpointCoords(this.posX, this.posY + this.height - 0.1, this.posZ, + target.posX, target.posY + target.height/2, target.posZ); + worldObj.spawnEntityInWorld(arc); + }else{ + for(int j=0;j<8;j++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, target.posX + worldObj.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + worldObj.rand.nextFloat()*2 - 1, target.posZ + worldObj.rand.nextFloat() - 0.5, 0, 0, 0, 3); + worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, target.posX + rand.nextFloat(), target.getEntityBoundingBox().minY + target.height/2 + rand.nextFloat(), target.posZ + rand.nextFloat(), 0, 0, 0); + } + } + + target.playSound(WizardrySounds.SPELL_SPARK, 1.0F, rand.nextFloat() * 0.4F + 1.5F); + + if(this.getCaster() != null){ + WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.SHOCK), 6*damageMultiplier); + WizardryUtilities.applyStandardKnockback(this, target); + }else{ + target.attackEntityFrom(DamageSource.magic, 6*damageMultiplier); + } + } + } + } + } + } + } + + @Override + public void despawn(){ + + this.playSound(SoundEvents.ENTITY_GENERIC_EXPLODE, 1.0F, 1.0f); + + if(this.worldObj.isRemote){ + this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0); + } + + super.despawn(); + } + + @Override + public void fall(float distance, float damageMultiplier) { + + if(worldObj.isRemote){ + for(int i=0; i<40; i++){ + double particleX = this.posX - 1.0d + 2*rand.nextDouble(); + double particleZ = this.posZ - 1.0d + 2*rand.nextDouble(); + // Roundabout way of getting a block instance for the block the hammer is standing on (if any). + IBlockState block = worldObj.getBlockState(new BlockPos(this.posX, this.posY-2, this.posZ)); + + if(block != null){ + worldObj.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, this.posY, particleZ, + particleX - this.posX, 0, particleZ - this.posZ, Block.getStateId(block)); + } + } + }else{ + // Just to check the hammer has actually fallen from the sky, rather than the block under it being broken. + if(this.fallDistance > 10){ + EntityLightningBolt entitylightning = new EntityLightningBolt(worldObj, this.posX, this.posY, this.posZ, false); + worldObj.addWeatherEffect(entitylightning); + } + } + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound) + { + super.writeEntityToNBT(nbttagcompound); + nbttagcompound.setByte("Time", (byte)this.fallTime); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound) + { + super.readEntityFromNBT(nbttagcompound); + this.fallTime = nbttagcompound.getByte("Time") & 255; + } + + @Override + public boolean isInRangeToRenderDist(double distance) { + return true; + } +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityHealAura.java b/src/main/java/electroblob/wizardry/entity/construct/EntityHealAura.java new file mode 100644 index 00000000..34a96dd8 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityHealAura.java @@ -0,0 +1,85 @@ +package electroblob.wizardry.entity.construct; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.DamageSource; +import net.minecraft.world.World; + +public class EntityHealAura extends EntityMagicConstruct { + + public EntityHealAura(World world) { + super(world); + this.height = 1.0f; + this.width = 5.0f; + } + + public EntityHealAura(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) { + super(world, x, y, z, caster, lifetime, damageMultiplier); + this.height = 1.0f; + this.width = 5.0f; + } + + public void onUpdate(){ + + if(this.ticksExisted % 25 == 1){ + this.playSound(WizardrySounds.SPELL_LOOP_SPARKLE, 0.1f, 1.0f); + } + + super.onUpdate(); + + if(!this.worldObj.isRemote){ + + List targets = WizardryUtilities.getEntitiesWithinRadius(2.5d, this.posX, this.posY, this.posZ, this.worldObj); + + for(EntityLivingBase target : targets){ + + if(this.isValidTarget(target)){ + + if(target.isEntityUndead()){ + + double velX = target.motionX; + double velY = target.motionY; + double velZ = target.motionZ; + + if(this.getCaster() != null){ + target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.RADIANT), 1*damageMultiplier); + }else{ + target.attackEntityFrom(DamageSource.magic, 1*damageMultiplier); + } + + // Removes knockback + target.motionX = velX; + target.motionY = velY; + target.motionZ = velZ; + } + + }else if(target.getHealth() < target.getMaxHealth() && this.ticksExisted % 5 == 0){ + target.heal(1*damageMultiplier); + } + } + }else{ + for(int i=1; i<3; i++){ + float brightness = 0.5f + (rand.nextFloat()*0.5f); + double radius = rand.nextDouble()*2.0; + double angle = rand.nextDouble()*Math.PI*2; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX + radius*Math.cos(angle), this.posY, this.posZ + radius*Math.sin(angle), 0, 0.05f, 0, 48 + this.rand.nextInt(12), 1.0f, 1.0f, brightness); + } + } + } + + /** + * Return whether this entity should be rendered as on fire. + */ + public boolean canRenderOnFire() + { + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityIceSpike.java b/src/main/java/electroblob/wizardry/entity/construct/EntityIceSpike.java new file mode 100644 index 00000000..b314a2cf --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityIceSpike.java @@ -0,0 +1,50 @@ +package electroblob.wizardry.entity.construct; + +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.potion.PotionEffect; +import net.minecraft.world.World; + +public class EntityIceSpike extends EntityMagicConstruct { + + public EntityIceSpike(World world) { + super(world); + this.setSize(0.5f, 1.0f); + } + + public EntityIceSpike(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier){ + super(world, x, y, z, caster, lifetime, damageMultiplier); + this.setSize(0.5f, 1.0f); + } + + public void onUpdate(){ + + if(lifetime - this.ticksExisted < 15){ + this.motionY = -0.01*(this.ticksExisted-(lifetime-15)); + }else if(lifetime - this.ticksExisted < 25){ + this.motionY = 0; + }else if(lifetime - this.ticksExisted < 28){ + this.motionY = 0.25; + } + + this.moveEntity(this.motionX, this.motionY, this.motionZ); + + if(lifetime - this.ticksExisted == 30) this.playSound(WizardrySounds.SPELL_ICE, 1, 2); + + if(!this.worldObj.isRemote){ + for(Object entity : this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox())){ + if(entity instanceof EntityLivingBase && this.isValidTarget((EntityLivingBase)entity)){ + // Potion effect only gets added if the damage succeeded. + if(((EntityLivingBase)entity).attackEntityFrom(MagicDamage.causeDirectMagicDamage(this.getCaster(), DamageType.FROST), 5*this.damageMultiplier)) + ((EntityLivingBase)entity).addPotionEffect(new PotionEffect(WizardryPotions.frost, 100, 0)); + } + } + } + + super.onUpdate(); + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityLightningPulse.java b/src/main/java/electroblob/wizardry/entity/construct/EntityLightningPulse.java new file mode 100644 index 00000000..59653c0b --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityLightningPulse.java @@ -0,0 +1,26 @@ +package electroblob.wizardry.entity.construct; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.world.World; + +public class EntityLightningPulse extends EntityMagicConstruct { + + public EntityLightningPulse(World world) { + super(world); + this.setSize(6, 0.2f); + } + + public EntityLightningPulse(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) { + super(world, x, y, z, caster, lifetime, damageMultiplier); + this.setSize(6, 0.2f); + } + + /** + * Return whether this entity should be rendered as on fire. + */ + public boolean canRenderOnFire() + { + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityLightningSigil.java b/src/main/java/electroblob/wizardry/entity/construct/EntityLightningSigil.java new file mode 100644 index 00000000..8ecb646d --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityLightningSigil.java @@ -0,0 +1,125 @@ +package electroblob.wizardry.entity.construct; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.EntityArc; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.world.World; + +public class EntityLightningSigil extends EntityMagicConstruct { + + public EntityLightningSigil(World par1World) { + super(par1World); + this.height = 0.2f; + this.width = 2.0f; + } + + public EntityLightningSigil(World par1World, double x, double y, double z, EntityLivingBase caster, float damageMultiplier) { + super(par1World, x, y, z, caster, -1, damageMultiplier); + this.height = 0.2f; + this.width = 2.0f; + } + + // Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow + // it to stick in blocks. + public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9) + { + this.setPosition(par1, par3, par5); + this.setRotation(par7, par8); + } + + public void onUpdate(){ + + super.onUpdate(); + + if(this.ticksExisted > 600 && this.getCaster() == null && !this.worldObj.isRemote){ + this.setDead(); + } + + //if(!this.worldObj.isRemote){ + + List targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY, this.posZ, this.worldObj); + + for(EntityLivingBase target : targets){ + + if(this.isValidTarget(target)){ + + double velX = target.motionX; + double velY = target.motionY; + double velZ = target.motionZ; + + // Only works if target is actually damaged to account for hurtResistantTime + if(target.attackEntityFrom(getCaster() != null ? MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.SHOCK) : DamageSource.magic, 6)){ + + // Removes knockback + target.motionX = velX; + target.motionY = velY; + target.motionZ = velZ; + + this.playSound(WizardrySounds.SPELL_SPARK, 1.0f, 1.0f); + + // Secondary chaining effect + double seekerRange = 5.0d; + + List secondaryTargets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, target.posX, target.posY + target.height/2, target.posZ, worldObj); + + for(int j=0;j + * When extending this class, override both constructors. Generally speaking, subclasses of this class are areas + * of effect which deal damage or apply effects over time. + * @since Wizardry 1.0 + */ +public abstract class EntityMagicConstruct extends Entity implements IEntityAdditionalSpawnData { + + /** The entity that created this construct */ + private WeakReference caster; + + /** The UUID of the caster. Note that this is only for loading purposes; during normal updates + * the actual entity instance is stored (so that getEntityByUUID is not called constantly), + * so this will not always be synced (this is why it is private). */ + private UUID casterUUID; + + /** The time in ticks this magical construct lasts for; defaults to 600 (30 seconds). If this is -1 the construct + * doesn't despawn. */ + public int lifetime = 600; + + /** The damage multiplier for this construct, determined by the wand with which it was cast. */ + public float damageMultiplier = 1.0f; + + public EntityMagicConstruct(World par1World) { + super(par1World); + this.height = 1.0f; + this.width = 1.0f; + this.noClip = true; + } + + public EntityMagicConstruct(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) { + super(world); + this.height = 1.0f; + this.width = 1.0f; + this.setPosition(x, y, z); + this.caster = new WeakReference(caster); + this.noClip = true; + this.lifetime = lifetime; + this.damageMultiplier = damageMultiplier; + } + + // Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow + // it to stick in blocks. + @Override + public void setPositionAndRotationDirect(double x, double y, double z, float yaw, float pitch, int posRotationIncrements, boolean teleport) + { + this.setPosition(x, y, z); + this.setRotation(yaw, pitch); + } + + public void onUpdate(){ + + if(this.getCaster() == null && this.casterUUID != null){ + Entity entity = WizardryUtilities.getEntityByUUID(worldObj, casterUUID); + if(entity instanceof EntityLivingBase){ + this.caster = new WeakReference((EntityLivingBase)entity); + } + } + + if(this.ticksExisted > lifetime && lifetime != -1){ + this.despawn(); + } + + super.onUpdate(); + + } + + /** + * Defaults to just setDead() in EntityMagicConstruct, but is provided to allow subclasses to override this + * e.g. bubble uses it to dismount the entity inside it and play the 'pop' sound before calling super(). You + * should always call super() when overriding this method, in case it changes. There is no need, therefore, to + * call setDead() when overriding. + */ + public void despawn(){ + this.setDead(); + } + + @Override + protected void entityInit() { + + } + + @Override + protected void readEntityFromNBT(NBTTagCompound nbttagcompound){ + casterUUID = nbttagcompound.getUniqueId("casterUUID"); + lifetime = nbttagcompound.getInteger("lifetime"); + damageMultiplier = nbttagcompound.getFloat("damageMultiplier"); + } + + @Override + protected void writeEntityToNBT(NBTTagCompound nbttagcompound){ + if(this.getCaster() != null){ + nbttagcompound.setUniqueId("casterUUID", this.getCaster().getUniqueID()); + } + nbttagcompound.setInteger("lifetime", lifetime); + nbttagcompound.setFloat("damageMultiplier", damageMultiplier); + } + + @Override + public void writeSpawnData(ByteBuf data){ + data.writeInt(lifetime); + } + + @Override + public void readSpawnData(ByteBuf data){ + lifetime = data.readInt(); + } + + /** + * Returns the EntityLivingBase that created this construct, or null if it no longer exists. Cases where the + * entity may no longer exist are: entity died or was deleted, mob despawned, player logged out, entity teleported + * to another dimension, or this construct simply had no caster in the first place. + */ + public EntityLivingBase getCaster() { + return caster == null ? null : caster.get(); + } + + /** + * Shorthand for {@link WizardryUtilities#isValidTarget(Entity, Entity)}, with the owner of this construct as the + * attacker. Also allows subclasses to override it if they wish to do so. + */ + public boolean isValidTarget(Entity target){ + return WizardryUtilities.isValidTarget(this.getCaster(), target); + } + + @Override + public boolean canRenderOnFire() + { + return false; + } + + @Override + public boolean isPushedByWater() { + return false; + } +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityTornado.java b/src/main/java/electroblob/wizardry/entity/construct/EntityTornado.java new file mode 100644 index 00000000..2e1625a9 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityTornado.java @@ -0,0 +1,184 @@ +package electroblob.wizardry.entity.construct; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardryAchievements; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import io.netty.buffer.ByteBuf; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.passive.EntityPig; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.play.server.SPacketEntityVelocity; +import net.minecraft.util.DamageSource; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class EntityTornado extends EntityMagicConstruct { + + private double velX, velZ; + + public EntityTornado(World world) { + super(world); + this.height = 8.0f; + this.width = 5.0f; + this.isImmuneToFire = false; + } + + public EntityTornado(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, double velX, double velZ, float damageMultiplier) { + super(world, x, y, z, caster, lifetime, damageMultiplier); + this.height = 8.0f; + this.width = 5.0f; + this.velX = velX; + this.velZ = velZ; + this.isImmuneToFire = false; + } + + public void onUpdate(){ + + super.onUpdate(); + + if(this.ticksExisted % 120 == 1 && worldObj.isRemote){ + // Repeat is false so that the sound fades out when the tornado does rather than stopping suddenly + Wizardry.proxy.playMovingSound(this, WizardrySounds.SPELL_LOOP_WIND, 1.0f, 1.0f, false); + } + + this.moveEntity(velX, motionY, velZ); + + BlockPos pos = new BlockPos(this); + int y = WizardryUtilities.getNearestFloorLevelC(worldObj, pos.up(3), 5); + pos = new BlockPos(pos.getX(), y, pos.getZ()); + + if(this.worldObj.getBlockState(pos).getMaterial() == Material.LAVA){ + // Fire tornado! + this.setFire(5); + } + + if(!this.worldObj.isRemote){ + + List targets = WizardryUtilities.getEntitiesWithinRadius(4.0d, this.posX, this.posY, this.posZ, this.worldObj); + + for(EntityLivingBase target : targets){ + + if(this.isValidTarget(target)){ + + double velY = target.motionY; + + double dx = this.posX-target.posX > 0? 0.5 - (this.posX-target.posX)/8 : -0.5 - (this.posX-target.posX)/8; + double dz = this.posZ-target.posZ > 0? 0.5 - (this.posZ-target.posZ)/8 : -0.5 - (this.posZ-target.posZ)/8; + + if(this.isBurning()){ + target.setFire(4); + } + + if(this.getCaster() != null){ + target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC), 1*damageMultiplier); + }else{ + target.attackEntityFrom(DamageSource.magic, 1*damageMultiplier); + } + + target.motionX = dx; + target.motionY = velY+0.2; + target.motionZ = dz; + + // Player motion is handled on that player's client so needs packets + if(target instanceof EntityPlayerMP){ + ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + } + + // The 'Not Again...' achievement + if(target instanceof EntityPig && WizardryUtilities.getRider(target) instanceof EntityPlayer){ + ((EntityPlayer)WizardryUtilities.getRider(target)).addStat(WizardryAchievements.pig_tornado); + } + } + } + }else{ + for(int i=1; i<10; i++){ + + double yPos = rand.nextDouble()*8; + + int blockX = (int)this.posX - 2 + this.rand.nextInt(4); + int blockZ = (int)this.posZ - 2 + this.rand.nextInt(4); + + BlockPos pos1 = new BlockPos(blockX, this.posY+3, blockZ); + + int blockY = WizardryUtilities.getNearestFloorLevelC(worldObj, pos1, 5) - 1; + + pos1 = new BlockPos(pos1.getX(), blockY, pos1.getZ()); + + IBlockState block = this.worldObj.getBlockState(pos1); + + // If the block it found was air or something it can't pick up, it makes a best guess based on the biome. + if(!canTornadoPickUpBitsOf(block)){ + block = worldObj.getBiome(pos1).topBlock; + } + + Wizardry.proxy.spawnTornadoParticle(worldObj, this.posX, this.posY + yPos, this.posZ, this.velX, this.velZ, yPos/3 + 0.5d, 100, block, pos1); + Wizardry.proxy.spawnTornadoParticle(worldObj, this.posX, this.posY + yPos, this.posZ, this.velX, this.velZ, yPos/3 + 0.5d, 100, block, pos1); + + // Sometimes spawns leaf particles if the block is leaves + if(block.getMaterial() == Material.LEAVES && this.rand.nextInt(3) == 0){ + double yPos1 = rand.nextDouble()*8; + Wizardry.proxy.spawnParticle(WizardryParticleType.LEAF, worldObj, this.posX + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), this.posY + yPos1, this.posZ + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), 0, -0.05, 0, 40 + rand.nextInt(10)); + } + + // Sometimes spawns snow particles if the block is snow + if(block.getMaterial() == Material.SNOW || block.getMaterial() == Material.CRAFTED_SNOW && this.rand.nextInt(3) == 0){ + double yPos1 = rand.nextDouble()*8; + Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, worldObj, this.posX + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), this.posY + yPos1, this.posZ + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), 0, -0.02, 0, 40 + rand.nextInt(10)); + } + } + } + } + + private static boolean canTornadoPickUpBitsOf(IBlockState block){ + Material material = block.getMaterial(); + return material == Material.CRAFTED_SNOW + || material == Material.GROUND + || material == Material.GRASS + || material == Material.LAVA + || material == Material.SAND + || material == Material.SNOW + || material == Material.WATER + || material == Material.PLANTS + || material == Material.LEAVES + || material == Material.VINE; + } + + @Override + protected void readEntityFromNBT(NBTTagCompound nbttagcompound) { + super.readEntityFromNBT(nbttagcompound); + velX = nbttagcompound.getDouble("velX"); + velZ = nbttagcompound.getDouble("velZ"); + } + + @Override + protected void writeEntityToNBT(NBTTagCompound nbttagcompound) { + super.writeEntityToNBT(nbttagcompound); + nbttagcompound.setDouble("velX", velX); + nbttagcompound.setDouble("velZ", velZ); + } + + @Override + public void writeSpawnData(ByteBuf data) { + super.writeSpawnData(data); + data.writeDouble(velX); + data.writeDouble(velZ); + } + + @Override + public void readSpawnData(ByteBuf data) { + super.readSpawnData(data); + this.velX = data.readDouble(); + this.velZ = data.readDouble(); + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityAIAttackSpell.java b/src/main/java/electroblob/wizardry/entity/living/EntityAIAttackSpell.java new file mode 100644 index 00000000..07222f34 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityAIAttackSpell.java @@ -0,0 +1,211 @@ +package electroblob.wizardry.entity.living; + +import java.util.ArrayList; +import java.util.List; + +import electroblob.wizardry.packet.PacketNPCCastSpell; +import electroblob.wizardry.packet.WizardryPacketHandler; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.ai.EntityAIBase; +import net.minecraft.util.EnumHand; +import net.minecraftforge.fml.common.network.NetworkRegistry.TargetPoint; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; + +/** Entity AI class for use by instances of {@link ISpellCaster}. This deals with pathing, the spell casting itself + * and the attack cooldown. Also provides an automatic implementation of continuous spell casting using the methods + * specified in {@code ISpellCaster}; all the entity class needs to do is implement those methods. */ +public class EntityAIAttackSpell extends EntityAIBase { + + /** The entity the AI instance has been applied to. */ + private final EntityLiving attacker; + /** The entity the AI instance has been applied to, but as an ISpellCaster. */ + private final ISpellCaster caster; + /** The tagret to be attacked. */ + private EntityLivingBase target; + /** Decremented each tick while greater than 0. When a spell is cast, this is set to that spell's cooldown plus + * the base cooldown. */ + private int cooldown; + /** The number of ticks between the entity finding a new target and when it first starts attacking, and also the + * amount that is added to the spell's cooldown between casting spells. */ + private final int baseCooldown; + /** Decremented each tick while greater than 0. When a continuous spell is first cast, this is set to + * the value of {@link EntityAIAttackSpell#continuousSpellDuration}. */ + // I think that in this case this is only necessary on the server side. If any inconsistent behaviour + // occurs, look into syncing this as well. + private int continuousSpellTimer; + /** The number of ticks that continuous spells will be cast for before cooling down. */ + private final int continuousSpellDuration; + /** The speed that the entity should move when attacking. Only used when passed into the navigator. */ + private final double speed; + private int seeTime; + private final float maxAttackDistance; + + /** + * Creates a new spell attack AI with the given parameters. + * @param attacker The entity that that uses this AI. + * @param speed The speed that the entity should move when attacking. Only used when passed into the navigator. + * @param maxDistance The maximum distance the entity should be from its target. + * @param baseCooldown The number of ticks between the entity finding a new target and when it first starts attacking, + * and also the amount that is added to the cooldown of the spell that has just been cast. + * @param continuousSpellDuration The number of ticks that continuous spells will be cast for before cooling down. + */ + public EntityAIAttackSpell(ISpellCaster attacker, double speed, float maxDistance, int baseCooldown, int continuousSpellDuration){ + + this.cooldown = -1; + + if(!(attacker instanceof EntityLiving)){ + throw new IllegalArgumentException("Tried to create an EntityAICastSpell for an entity that isn't an EntityLiving"); + }else{ + this.caster = attacker; + this.attacker = (EntityLiving)attacker; + this.baseCooldown = baseCooldown; + this.continuousSpellDuration = continuousSpellDuration; + this.speed = speed; + this.maxAttackDistance = maxDistance * maxDistance; + this.setMutexBits(3); + } + } + + @Override + public boolean shouldExecute(){ + + EntityLivingBase entitylivingbase = this.attacker.getAttackTarget(); + + if(entitylivingbase == null){ + return false; + }else{ + this.target = entitylivingbase; + return true; + } + } + + @Override + public boolean continueExecuting(){ + return this.shouldExecute() || !this.attacker.getNavigator().noPath(); + } + + @Override + public void resetTask(){ + this.target = null; + this.seeTime = 0; + this.cooldown = -1; + this.setContinuousSpellAndNotify(Spells.none, new SpellModifiers()); + this.continuousSpellTimer = 0; + } + + private void setContinuousSpellAndNotify(Spell spell, SpellModifiers modifiers){ + caster.setContinuousSpell(spell); + WizardryPacketHandler.net.sendToAllAround(new PacketNPCCastSpell.Message(attacker.getEntityId(), + target == null ? -1 : target.getEntityId(), EnumHand.MAIN_HAND, spell.id(), modifiers), + // Particles are usually only visible from 16 blocks away, so 128 is more than far enough. + new TargetPoint(attacker.dimension, attacker.posX, attacker.posY, attacker.posZ, 128)); + } + + @Override + public void updateTask(){ + + // Only executed server side. + + double distanceSq = this.attacker.getDistanceSq(this.target.posX, this.target.getEntityBoundingBox().minY, this.target.posZ); + boolean targetIsVisible = this.attacker.getEntitySenses().canSee(this.target); + + if(targetIsVisible){ + ++this.seeTime; + }else{ + this.seeTime = 0; + } + + if(distanceSq <= (double)this.maxAttackDistance && this.seeTime >= 20){ + this.attacker.getNavigator().clearPathEntity(); + }else{ + this.attacker.getNavigator().tryMoveToEntityLiving(this.target, this.speed); + } + + this.attacker.getLookHelper().setLookPositionWithEntity(this.target, 30.0F, 30.0F); + + if(this.continuousSpellTimer > 0){ + + this.continuousSpellTimer--; + + if(distanceSq > (double)this.maxAttackDistance || !targetIsVisible + || !caster.getContinuousSpell().cast(attacker.worldObj, attacker, EnumHand.MAIN_HAND, + this.continuousSpellDuration - this.continuousSpellTimer, target, caster.getModifiers()) + || this.continuousSpellTimer == 0){ + // If the spell no longer succeeds, the target goes out of range or sight, or the time has elapsed, + // reset the continuous spell timer and start the cooldown. + this.continuousSpellTimer = 0; + setContinuousSpellAndNotify(Spells.none, new SpellModifiers()); + this.cooldown = this.baseCooldown; + return; + } + + }else if(--this.cooldown == 0){ + + if(distanceSq > (double)this.maxAttackDistance || !targetIsVisible){ + return; + } + + if(!attacker.isPotionActive(WizardryPotions.arcane_jammer)){ + + double dx = target.posX - attacker.posX; + double dz = target.posZ - attacker.posZ; + + List spells = new ArrayList(caster.getSpells()); + + if(spells.size() > 0){ + + if(!attacker.worldObj.isRemote){ + + // New way of choosing a spell; keeps trying until one works or all have been tried + + Spell spell; + + while(!spells.isEmpty()){ + + spell = spells.get(attacker.worldObj.rand.nextInt(spells.size())); + + SpellModifiers modifiers = caster.getModifiers(); + + if(spell != null && spell.cast(attacker.worldObj, attacker, EnumHand.MAIN_HAND, 0, target, modifiers)){ + + if(spell.isContinuous){ + // -1 because the spell has been cast once already! + this.continuousSpellTimer = this.continuousSpellDuration - 1; + setContinuousSpellAndNotify(spell, modifiers); + + }else{ + // For now, the cooldown is just added to the constant base cooldown. I think this + // is a reasonable way of doing things; it's certainly better than before. + this.cooldown = this.baseCooldown + spell.cooldown; + + if(spell.doesSpellRequirePacket()){ + // Sends a packet to all players in dimension to tell them to spawn particles. + IMessage msg = new PacketNPCCastSpell.Message(attacker.getEntityId(), + target.getEntityId(), EnumHand.MAIN_HAND, spell.id(), modifiers); + WizardryPacketHandler.net.sendToDimension(msg, attacker.worldObj.provider.getDimension()); + } + } + + attacker.rotationYaw = (float)(Math.atan2(dz, dx) * 180.0D / Math.PI) - 90.0F; + + return; + + }else{ + spells.remove(spell); + } + } + } + } + } + + }else if(this.cooldown < 0){ + // This should only be reached when the entity first starts attacking. Stops it attacking instantly. + this.cooldown = this.baseCooldown; + } + } +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityAISelectSpell.java b/src/main/java/electroblob/wizardry/entity/living/EntityAISelectSpell.java new file mode 100644 index 00000000..286dc93c --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityAISelectSpell.java @@ -0,0 +1,14 @@ +package electroblob.wizardry.entity.living; + +import net.minecraft.entity.ai.EntityAIBase; + +public class EntityAISelectSpell extends EntityAIBase { + + // TODO: Write this class + + @Override + public boolean shouldExecute(){ + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityBlazeMinion.java b/src/main/java/electroblob/wizardry/entity/living/EntityBlazeMinion.java new file mode 100644 index 00000000..473f30b7 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityBlazeMinion.java @@ -0,0 +1,160 @@ +package electroblob.wizardry.entity.living; + +import java.lang.ref.WeakReference; +import java.util.UUID; + +import electroblob.wizardry.Wizardry; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; +import net.minecraft.entity.monster.EntityBlaze; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +public class EntityBlazeMinion extends EntityBlaze implements ISummonedCreature { + + // Field implementations + private int lifetime = 600; + private WeakReference casterReference; + private UUID casterUUID; + + // Setter + getter implementations + @Override public int getLifetime(){ return lifetime; } + @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } + @Override public WeakReference getCasterReference(){ return casterReference; } + @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } + @Override public UUID getCasterUUID() { return casterUUID; } + @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } + + /** + * Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't + * matter because the client side entity immediately gets the lifetime value copied over to it by this class + * anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's + * no need to do anything inside it other than call super(). + */ + public EntityBlazeMinion(World world){ + super(world); + this.experienceValue = 0; + } + + /** + * Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when + * extending this class (be sure to call super()) so that AI and other things can be added. + */ + public EntityBlazeMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world); + this.setPosition(x, y, z); + this.casterReference = new WeakReference(caster); + this.experienceValue = 0; + this.lifetime = lifetime; + } + + // EntityBlaze overrides + + // This particular override is pretty standard: let the superclass handle basic AI like swimming, but replace its + // targeting system with one that targets hostile mobs and takes the ADS into account. + @Override + protected void initEntityAI() + { + super.initEntityAI(); + this.targetTasks.taskEntries.clear(); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, + 0, false, true, this.getTargetSelector())); + } + + // Implementations + + @Override + public void setRevengeTarget(EntityLivingBase entity){ + if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); + } + + @Override + public void onUpdate(){ + super.onUpdate(); + this.updateDelegate(); + } + + @Override + public void onSpawn(){ + this.spawnParticleEffect(); + } + + @Override + public void onDespawn(){ + this.spawnParticleEffect(); + } + + /** Normally this would be private, but since this class has subclasses with different spawn/despawn particle + * effects, it makes sense to have them override this rather than both onSpawn() and onDespawn(). */ + protected void spawnParticleEffect(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + this.worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); + } + } + } + + @Override + public boolean hasParticleEffect() { + return false; + } + + @Override + protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { + // In this case, the delegate method determines whether super is called. + // Rather handily, we can make use of Java's 'stop as soon as you find true' method of evaluating OR statements. + return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + this.writeNBTDelegate(nbttagcompound); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + this.writeNBTDelegate(nbttagcompound); + } + + // Recommended overrides + + @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } + @Override protected boolean canDropLoot(){ return false; } + @Override protected Item getDropItem(){ return null; } + @Override protected ResourceLocation getLootTable(){ return null; } + // This vanilla method has nothing to do with the custom despawn() method. + @Override protected boolean canDespawn(){ return false; } + + @Override + public boolean canAttackClass(Class entityType){ + return true; + } + + @Override + public ITextComponent getDisplayName(){ + if(getCaster() != null){ + return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(), + new TextComponentTranslation("entity." + this.getEntityString() + ".name")); + }else{ + return super.getDisplayName(); + } + } + + @Override + public boolean hasCustomName(){ + // If this returns true, the renderer will show the nameplate when looking directly at the entity + return Wizardry.settings.showSummonedCreatureNames && getCaster() != null; + } +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityDecoy.java b/src/main/java/electroblob/wizardry/entity/living/EntityDecoy.java new file mode 100644 index 00000000..f4d9a423 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityDecoy.java @@ -0,0 +1,107 @@ +package electroblob.wizardry.entity.living; + +import java.lang.ref.WeakReference; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.WizardryParticleType; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAILookIdle; +import net.minecraft.entity.ai.EntityAISwimming; +import net.minecraft.entity.ai.EntityAIWander; +import net.minecraft.entity.ai.EntityAIWatchClosest; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.DamageSource; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.world.World; + +public class EntityDecoy extends EntitySummonedCreature { + + public EntityDecoy(World world){ + super(world); + } + + public EntityDecoy(World world, double x, double y, double z, EntityLivingBase caster, int lifetime) { + super(world, x, y, z, caster, lifetime); + this.setAlwaysRenderNameTag(caster instanceof EntityPlayer); + } + + @Override + protected void initEntityAI() { + // Decoys just wander around aimlessly, watching anything living. + this.tasks.addTask(0, new EntityAISwimming(this)); + this.tasks.addTask(1, new EntityAIWander(this, 1.0D)); + this.tasks.addTask(2, new EntityAIWatchClosest(this, EntityLivingBase.class, 6.0F)); + this.tasks.addTask(3, new EntityAILookIdle(this)); + } + + @Override + public void onDespawn(){ + super.onDespawn(); + for(int i=0; i<20; i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, worldObj, this.posX + (this.rand.nextDouble()-0.5)*this.width, + this.getEntityBoundingBox().minY + this.rand.nextDouble()*this.height, this.posZ + (this.rand.nextDouble()-0.5)*this.width, + 0, 0, 0, 40, 0.2f, 1.0f, 0.8f); + } + } + + @Override + public boolean isEntityInvulnerable(DamageSource source){ + return true; + } + + @Override + public boolean isSneaking(){ + return false; + } + + @Override + public void onUpdate() { + super.onUpdate(); + if(this.getCaster() == null || this.getCaster().isDead){ + this.setDead(); + this.onDespawn(); + } + } + + @Override + protected void applyEntityAttributes() + { + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25D); + } + + @Override + public ITextComponent getDisplayName(){ + if(getCaster() instanceof EntityPlayer){ + return this.getCaster().getDisplayName(); + }else{ + return super.getDisplayName(); + } + } + + @Override + public boolean hasCustomName(){ + return getCaster() instanceof EntityPlayer; + } + + @Override + public boolean hasRangedAttack() { + return false; + } + + @Override + public void writeSpawnData(ByteBuf data){ + super.writeSpawnData(data); + if(this.getCaster() != null) data.writeInt(this.getCaster().getEntityId()); + } + + @Override + public void readSpawnData(ByteBuf data){ + super.readSpawnData(data); + if(!data.isReadable()) return; + this.setCasterReference(new WeakReference((EntityLivingBase)this.worldObj.getEntityByID(data.readInt()))); + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityEvilWizard.java b/src/main/java/electroblob/wizardry/entity/living/EntityEvilWizard.java new file mode 100644 index 00000000..3e10cd47 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityEvilWizard.java @@ -0,0 +1,364 @@ +package electroblob.wizardry.entity.living; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import com.google.common.base.Predicate; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardryAchievements; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.IEntityLivingData; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; +import net.minecraft.entity.ai.EntityAIOpenDoor; +import net.minecraft.entity.ai.EntityAIRestrictOpenDoor; +import net.minecraft.entity.ai.EntityAISwimming; +import net.minecraft.entity.ai.EntityAIWander; +import net.minecraft.entity.ai.EntityAIWatchClosest2; +import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagInt; +import net.minecraft.network.datasync.DataParameter; +import net.minecraft.network.datasync.DataSerializers; +import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.pathfinding.PathNavigateGround; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumHand; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundEvent; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.World; +import net.minecraftforge.common.util.Constants.NBT; +import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; + +public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntityAdditionalSpawnData { + + private EntityAIAttackSpell spellCastingAI = new EntityAIAttackSpell(this, 0.5D, 14.0F, 30, 50); + + public int textureIndex = 0; + + public boolean hasTower = false; + + /** The entity selector passed into the new AI methods. */ + protected Predicate targetSelector; + + /** Data parameter for the cooldown time for wizards healing themselves. */ + private static final DataParameter HEAL_COOLDOWN = EntityDataManager.createKey(EntityWizard.class, DataSerializers.VARINT); + /** Data parameter for the wizard's element. */ + private static final DataParameter ELEMENT = EntityDataManager.createKey(EntityEvilWizard.class, DataSerializers.VARINT); + /** The resource location for the evil wizard's loot table. */ + private static final ResourceLocation LOOT_TABLE = new ResourceLocation(Wizardry.MODID, "entities/evil_wizard"); + + // Field implementations + private List spells = new ArrayList(4); + private Spell continuousSpell; + + public EntityEvilWizard(World world){ + + super(world); + this.setSize(0.6F, 1.8F); + ((PathNavigateGround)this.getNavigator()).setBreakDoors(true); + + // For some reason this can't be done in initEntityAI + this.tasks.addTask(3, this.spellCastingAI); + + this.detachHome(); + } + + @Override + protected void entityInit(){ + super.entityInit(); + this.dataManager.register(HEAL_COOLDOWN, -1); + this.dataManager.register(ELEMENT, 0); + } + + @Override + protected void initEntityAI(){ + this.tasks.addTask(0, new EntityAISwimming(this)); + this.tasks.addTask(4, new EntityAIRestrictOpenDoor(this)); + this.tasks.addTask(5, new EntityAIOpenDoor(this, true)); + this.tasks.addTask(6, new EntityAIMoveTowardsRestriction(this, 0.6D)); + this.tasks.addTask(7, new EntityAIWatchClosest2(this, EntityPlayer.class, 3.0F, 1.0F)); + this.tasks.addTask(7, new EntityAIWander(this, 0.6D)); + + this.targetSelector = new Predicate(){ + + public boolean apply(Entity entity){ + + // If the target is valid and not invisible... + if(entity != null && !entity.isInvisible() && WizardryUtilities.isValidTarget(EntityEvilWizard.this, entity)){ + + //... and is a player, a summoned creature, another (non-evil) wizard ... + if(entity instanceof EntityPlayer || (entity instanceof ISummonedCreature || entity instanceof EntityWizard + // ... or in the whitelist ... + || Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))) + // ... and isn't in the blacklist ... + && !Arrays.asList(Wizardry.settings.summonedCreatureTargetsBlacklist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){ + // ... it can be attacked. + return true; + } + } + + return false; + } + }; + + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + this.targetTasks.addTask(0, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, + 0, false, true, this.targetSelector)); + } + + @Override + protected void applyEntityAttributes() + { + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.5D); + this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30); + } + + private int getHealCooldown(){ + return this.dataManager.get(HEAL_COOLDOWN); + } + + private void setHealCooldown(int cooldown){ + this.dataManager.set(HEAL_COOLDOWN, cooldown); + } + + public Element getElement(){ + return Element.values()[this.dataManager.get(ELEMENT)]; + } + + public void setElement(Element element){ + this.dataManager.set(ELEMENT, element.ordinal()); + } + + @Override + public List getSpells(){ + return this.spells; + } + + @Override + public SpellModifiers getModifiers(){ + return new SpellModifiers(); + } + + @Override + public void setContinuousSpell(Spell spell){ + this.continuousSpell = spell; + } + + @Override + public Spell getContinuousSpell(){ + return this.continuousSpell; + } + + @Override + public void onLivingUpdate(){ + + super.onLivingUpdate(); + + int healCooldown = this.getHealCooldown(); + + // This is now done slightly differently because isPotionActive doesn't work on client here, meaning that when + // affected with arcane jammer and healCooldown == 0, whilst the wizard didn't actually heal or play the sound, + // the particles still spawned, and since healCooldown wasn't reset they spawned every tick until the arcane + // jammer wore off. + if(healCooldown == 0 && this.getHealth() < this.getMaxHealth() && this.getHealth() > 0 && !this.isPotionActive(WizardryPotions.arcane_jammer)){ + + // Healer wizards use greater heal. + this.heal(this.getElement() == Element.HEALING ? 8 : 4); + this.setHealCooldown(-1); + + // deathTime == 0 checks the wizard isn't currently dying + }else if(healCooldown == -1 && this.deathTime == 0){ + + // Heal particles + if(worldObj.isRemote){ + for(int i=0; i<10; i++){ + double d0 = (double)((float)this.posX + rand.nextFloat()*2 - 1.0F); + // Apparently the client side spawns the particles 1 block higher than it should... hence the - 0.5F. + double d1 = (double)((float)this.posY - 0.5F + rand.nextFloat()); + double d2 = (double)((float)this.posZ + rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, d0, d1, d2, 0, 0.1F, 0, 48 + rand.nextInt(12), 1.0f, 1.0f, 0.3f); + } + }else{ + if(this.getHealth() < 10){ + // Wizard heals himself more often if he has low health + this.setHealCooldown(150); + }else{ + this.setHealCooldown(400); + } + + this.playSound(WizardrySounds.SPELL_HEAL, 0.7F, rand.nextFloat() * 0.4F + 1.0F); + } + } + if(healCooldown > 0){ + this.setHealCooldown(healCooldown - 1); + } + } + + @Override + protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack){ + + // Debugging + //player.addChatComponentMessage(new TextComponentTranslation("wizard.debug", Spell.get(spells[1]).getDisplayName(), Spell.get(spells[2]).getDisplayName(), Spell.get(spells[3]).getDisplayName())); + + // When right-clicked with a spell book in creative, sets one of the spells to that spell + if(player.capabilities.isCreativeMode && stack != null && stack.getItem() instanceof ItemSpellBook){ + if(this.spells.size() >= 4 && Spell.get(stack.getItemDamage()).canBeCastByNPCs()){ + this.spells.set(rand.nextInt(3)+1, Spell.get(stack.getItemDamage())); + return true; + } + } + + return false; + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbt){ + super.writeEntityToNBT(nbt); + nbt.setInteger("element", this.getElement().ordinal()); + nbt.setInteger("skin", this.textureIndex); + nbt.setTag("spells", WizardryUtilities.listToNBT(spells, spell -> new NBTTagInt(spell.id()))); + nbt.setBoolean("hasTower", this.hasTower); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbt){ + super.readEntityFromNBT(nbt); + this.setElement(Element.values()[nbt.getInteger("element")]); + this.textureIndex = nbt.getInteger("skin"); + this.spells = (List) WizardryUtilities.NBTToList(nbt.getTagList("spells", NBT.TAG_INT), + (NBTTagInt tag) -> Spell.get(tag.getInt())); + this.hasTower = nbt.getBoolean("hasTower"); + } + + @Override + protected boolean canDespawn(){ + // Evil wizards can only despawn if they don't have a tower (i.e. if they spawned naturally at night) + return !this.hasTower; + } + + @Override + protected float getSoundPitch(){ + return (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 0.6F; + } + + @Override + protected SoundEvent getAmbientSound() { + return SoundEvents.ENTITY_WITCH_AMBIENT; + } + + @Override + protected SoundEvent getHurtSound() + { + return SoundEvents.ENTITY_WITCH_HURT; + } + + @Override + protected SoundEvent getDeathSound() + { + return SoundEvents.ENTITY_WITCH_DEATH; + } + + // Although it *looks* like this is still called, in actual fact the only method that calls it is overridden in + // EntityLiving to use the loot table system instead. This has been kept as a fallback in case the loot table is + // not found. + @Override + protected void dropFewItems(boolean hitByPlayer, int lootingLevel){ + // Drops 3-5 crystals without looting bonuses + int j = 3 + this.rand.nextInt(3) + this.rand.nextInt(1 + lootingLevel); + + for(int k=0; k 0 && rand.nextInt(100) - lootingLevel < 5) this.entityDropItem(new ItemStack(WizardryItems.spell_book, 1, this.spells.get(1 + rand.nextInt(this.spells.size() - 1)).id()), 0); + } + + @Override + protected ResourceLocation getLootTable(){ + return LOOT_TABLE; + } + + @Override + public void onDeath(DamageSource source){ + + super.onDeath(source); + if(source.getEntity() instanceof EntityPlayer){ + ((EntityPlayer)source.getEntity()).addStat(WizardryAchievements.defeat_evil_wizard); + } + } + + @Override + public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData data){ + + data = super.onInitialSpawn(difficulty, data); + + textureIndex = this.rand.nextInt(6); + + if(rand.nextBoolean()){ + this.setElement(Element.values()[rand.nextInt(Element.values().length - 1) + 1]); + }else{ + this.setElement(Element.MAGIC); + } + + Element element = this.getElement(); + + // Adds armour. + for(EntityEquipmentSlot slot : WizardryUtilities.ARMOUR_SLOTS){ + this.setItemStackToSlot(slot, new ItemStack(WizardryUtilities.getArmour(element, slot))); + } + + // Default chance is 0.085f, for reference. + for(EntityEquipmentSlot slot : EntityEquipmentSlot.values()) this.setDropChance(slot, 0.0f); + + // All wizards know magic missile, even if it is disabled. + spells.add(Spells.magic_missile); + + Tier maxTier = EntityWizard.populateSpells(spells, element, 3, rand); + + // Now done after the spells so it can take the tier into account. For evil wizards this is slightly different; + // it picks a random wand which is at least a high enough tier for the spells the wizard has. + Tier tier = Tier.values()[maxTier.ordinal() + rand.nextInt(Tier.values().length - maxTier.ordinal())]; + this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(WizardryUtilities.getWand(tier, element))); + + return data; + } + + @Override + public void writeSpawnData(ByteBuf data){ + data.writeInt(textureIndex); + } + + @Override + public void readSpawnData(ByteBuf data){ + textureIndex = data.readInt(); + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityIceGiant.java b/src/main/java/electroblob/wizardry/entity/living/EntityIceGiant.java new file mode 100644 index 00000000..18d18e39 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityIceGiant.java @@ -0,0 +1,199 @@ +package electroblob.wizardry.entity.living; + +import java.lang.ref.WeakReference; +import java.util.UUID; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.EntityFlying; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.ai.EntityAIAttackMelee; +import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAILookIdle; +import net.minecraft.entity.ai.EntityAIMoveTowardsTarget; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; +import net.minecraft.entity.ai.EntityAIWatchClosest; +import net.minecraft.entity.monster.EntityIronGolem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.village.Village; +import net.minecraft.world.World; + +public class EntityIceGiant extends EntityIronGolem implements ISummonedCreature { + + // Field implementations + private int lifetime = 600; + private WeakReference casterReference; + private UUID casterUUID; + + // Setter + getter implementations + @Override public int getLifetime(){ return lifetime; } + @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } + @Override public WeakReference getCasterReference(){ return casterReference; } + @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } + @Override public UUID getCasterUUID() { return casterUUID; } + @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } + + /** + * Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't + * matter because the client side entity immediately gets the lifetime value copied over to it by this class + * anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's + * no need to do anything inside it other than call super(). + */ + public EntityIceGiant(World world){ + super(world); + this.setSize(1.4F, 2.9F); + this.experienceValue = 0; + } + + /** + * Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when + * extending this class (be sure to call super()) so that AI and other things can be added. + */ + public EntityIceGiant(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world); + this.setSize(1.4F, 2.9F); + this.setPosition(x, y, z); + this.casterReference = new WeakReference(caster); + this.experienceValue = 0; + this.lifetime = lifetime; + } + + @Override + protected void initEntityAI(){ + this.getNavigator().getNodeProcessor().setCanSwim(false); + this.tasks.addTask(1, new EntityAIAttackMelee(this, 1.0D, true)); + this.tasks.addTask(2, new EntityAIMoveTowardsTarget(this, 0.9D, 32.0F)); + //this.tasks.addTask(4, new EntityAIMoveTowardsRestriction(this, 1.0D)); + //this.tasks.addTask(5, new EntityAIWander(this, 0.6D)); + this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F)); + this.tasks.addTask(7, new EntityAILookIdle(this)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, + 0, false, true, this.getTargetSelector())); + } + + // EntityIronGolem overrides + + @Override protected void updateAITasks(){} // Disables home-checking + @Override public Village getVillage(){ return null; } + @Override public int getHoldRoseTick(){ return 0; } + + // Implementations + + @Override + public void setRevengeTarget(EntityLivingBase entity){ + if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); + } + + @Override + public void onUpdate(){ + super.onUpdate(); + this.updateDelegate(); + } + + @Override + public void onSpawn(){} + + @Override + public void onDespawn(){ + this.playSound(WizardrySounds.SPELL_FREEZE, 1.0f, 1.0f); + if(this.worldObj.isRemote){ + for(int i=0; i<30; i++){ + float brightness = 0.5f + (rand.nextFloat()/2); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, this.worldObj, this.posX-1 + rand.nextDouble()*2, this.posY+ rand.nextDouble()*3, this.posZ-1 + rand.nextDouble()*2, 0, -0.02, 0, 12 + rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); + } + } + } + + @Override + public void onLivingUpdate(){ + + super.onLivingUpdate(); + + if(this.worldObj.isRemote){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, this.worldObj, this.posX-1 + rand.nextDouble()*2, this.posY+ rand.nextDouble()*3, this.posZ-1 + rand.nextDouble()*2, 0, -0.02, 0, 40 + rand.nextInt(10)); + } + } + + @Override + public void onSuccessfulAttack(EntityLivingBase target){ + + target.motionY += 0.2; + target.motionX += this.getLookVec().xCoord*0.2; + target.motionZ += this.getLookVec().xCoord*0.2; + + target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 300, 0)); + + this.applyEnchantments(this, target); + + this.playSound(SoundEvents.ENTITY_IRONGOLEM_ATTACK, 1.0F, 1.0F); + } + + @Override + public boolean hasParticleEffect() { + return false; + } + + @Override + protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { + // In this case, the delegate method determines whether super is called. + // Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements. + return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + this.writeNBTDelegate(nbttagcompound); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + this.writeNBTDelegate(nbttagcompound); + } + + // Recommended overrides + + @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } + @Override protected boolean canDropLoot(){ return false; } + @Override protected Item getDropItem(){ return null; } + @Override protected ResourceLocation getLootTable(){ return null; } + @Override public boolean canPickUpLoot(){ return false; } + // This vanilla method has nothing to do with the custom onDespawn() method. + @Override protected boolean canDespawn(){ return false; } + + @Override + public boolean canAttackClass(Class entityType){ + // Returns true unless the given entity type is a flying entity. + return !EntityFlying.class.isAssignableFrom(entityType); + } + + @Override + public ITextComponent getDisplayName(){ + if(getCaster() != null){ + return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(), + new TextComponentTranslation("entity." + this.getEntityString() + ".name")); + }else{ + return super.getDisplayName(); + } + } + + @Override + public boolean hasCustomName(){ + // If this returns true, the renderer will show the nameplate when looking directly at the entity + return Wizardry.settings.showSummonedCreatureNames && getCaster() != null; + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityIceWraith.java b/src/main/java/electroblob/wizardry/entity/living/EntityIceWraith.java new file mode 100644 index 00000000..e7e6b6df --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityIceWraith.java @@ -0,0 +1,295 @@ +package electroblob.wizardry.entity.living; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.ai.EntityAIBase; +import net.minecraft.entity.ai.EntityAILookIdle; +import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction; +import net.minecraft.entity.ai.EntityAIWander; +import net.minecraft.entity.ai.EntityAIWatchClosest; +import net.minecraft.entity.monster.EntityBlaze; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.World; + +public class EntityIceWraith extends EntityBlazeMinion { + + /** The version from EntityLivingBase is only used in onLivingUpdate, so it can safely be copied. */ + private int jumpTicks; + + public EntityIceWraith(World world){ + super(world); + } + + public EntityIceWraith(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world, x, y, z, caster, lifetime); + this.isImmuneToFire = false; + } + + @Override + protected void initEntityAI(){ + super.initEntityAI(); + this.tasks.taskEntries.clear(); + this.tasks.addTask(4, new AIIceShardAttack(this)); + this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 1.0D)); + this.tasks.addTask(7, new EntityAIWander(this, 1.0D)); + this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F)); + this.tasks.addTask(8, new EntityAILookIdle(this)); + } + + @Override + protected void spawnParticleEffect() { + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + float brightness = 0.5f + (rand.nextFloat()/2); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, brightness + 0.1f, 1.0f); + } + } + } + + @Override + public void onLivingUpdate(){ + + if(!this.onGround && this.motionY < 0.0D){ + this.motionY *= 0.6D; + } + + if(this.rand.nextInt(24) == 0){ + this.playSound(WizardrySounds.SPELL_LOOP_WIND, 0.3F + this.rand.nextFloat()/4, this.rand.nextFloat() * 0.7F + 1.4F); + } + + if(this.worldObj.isRemote){ + for(int i = 0; i < 2; ++i){ + this.worldObj.spawnParticle(EnumParticleTypes.CLOUD, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0D, 0.0D, 0.0D); + } + } + + // Replaces super call. + this.livingBaseUpdate(); + } + + /** Copied from {@link EntityLivingBase#onLivingUpdate()}. The only change is removal of the updateElytra() call + * since that's irrelevant here. In actual fact, neither EntityMob nor EntityLiving has any code in its version + * of this method that is of use. This isn't exactly ideal, but it's the lesser of two evils since the + * alternative is copying the entire EntityBlaze class and its renderer. All to remove one particle effect... */ + // ... demonstrating why critical methods should delegate any non-critical functionality (like particles) to + // separate protected methods. + private void livingBaseUpdate(){ + + if (this.jumpTicks > 0) + { + --this.jumpTicks; + } + + if (this.newPosRotationIncrements > 0 && !this.canPassengerSteer()) + { + double d0 = this.posX + (this.interpTargetX - this.posX) / (double)this.newPosRotationIncrements; + double d1 = this.posY + (this.interpTargetY - this.posY) / (double)this.newPosRotationIncrements; + double d2 = this.posZ + (this.interpTargetZ - this.posZ) / (double)this.newPosRotationIncrements; + double d3 = MathHelper.wrapDegrees(this.interpTargetYaw - (double)this.rotationYaw); + this.rotationYaw = (float)((double)this.rotationYaw + d3 / (double)this.newPosRotationIncrements); + this.rotationPitch = (float)((double)this.rotationPitch + (this.interpTargetPitch - (double)this.rotationPitch) / (double)this.newPosRotationIncrements); + --this.newPosRotationIncrements; + this.setPosition(d0, d1, d2); + this.setRotation(this.rotationYaw, this.rotationPitch); + } + else if (!this.isServerWorld()) + { + this.motionX *= 0.98D; + this.motionY *= 0.98D; + this.motionZ *= 0.98D; + } + + if (Math.abs(this.motionX) < 0.003D) + { + this.motionX = 0.0D; + } + + if (Math.abs(this.motionY) < 0.003D) + { + this.motionY = 0.0D; + } + + if (Math.abs(this.motionZ) < 0.003D) + { + this.motionZ = 0.0D; + } + + this.worldObj.theProfiler.startSection("ai"); + + if (this.isMovementBlocked()) + { + this.isJumping = false; + this.moveStrafing = 0.0F; + this.moveForward = 0.0F; + this.randomYawVelocity = 0.0F; + } + else if (this.isServerWorld()) + { + this.worldObj.theProfiler.startSection("newAi"); + this.updateEntityActionState(); + this.worldObj.theProfiler.endSection(); + } + + this.worldObj.theProfiler.endSection(); + this.worldObj.theProfiler.startSection("jump"); + + if (this.isJumping) + { + if (this.isInWater()) + { + this.handleJumpWater(); + } + else if (this.isInLava()) + { + this.handleJumpLava(); + } + else if (this.onGround && this.jumpTicks == 0) + { + this.jump(); + this.jumpTicks = 10; + } + } + else + { + this.jumpTicks = 0; + } + + this.worldObj.theProfiler.endSection(); + this.worldObj.theProfiler.startSection("travel"); + this.moveStrafing *= 0.98F; + this.moveForward *= 0.98F; + this.randomYawVelocity *= 0.9F; + this.moveEntityWithHeading(this.moveStrafing, this.moveForward); + this.worldObj.theProfiler.endSection(); + this.worldObj.theProfiler.startSection("push"); + this.collideWithNearbyEntities(); + this.worldObj.theProfiler.endSection(); + } + + @Override + public boolean attackEntityFrom(DamageSource source, float amount){ + // Removes the damage from being wet that applies to blazes by checking if the mob is actually drowning. + if(source == DamageSource.drown && (this.getAir() > 0 || this.isPotionActive(MobEffects.WATER_BREATHING))){ + // In this case, the ice wraith is not actually drowning, so cancel the damage. + return false; + }else{ + return super.attackEntityFrom(source, amount); + } + } + + @Override + public boolean isBurning(){ + // Uses the datawatcher on both sides because fire is private to Entity (and I'm not using reflection here). + // TESTME: This should work, but there may be some issues with updating, so if it doesn't work, copy the + // version from Entity and use reflection to access the fire field. + return this.getFlag(0); + } + + /** Copied straight from EntityBlaze.AIFireballAttack, with the only changes being replacement of fireball + * spawning with a one-liner call to WizardryRegistry.iceShard.cast(...) and the removal of redundant local variables. */ + static class AIIceShardAttack extends EntityAIBase { + + private final EntityBlaze blaze; + private int attackStep; + private int attackTime; + + public AIIceShardAttack(EntityBlaze blazeIn) + { + this.blaze = blazeIn; + this.setMutexBits(3); + } + + /** + * Returns whether the EntityAIBase should begin execution. + */ + public boolean shouldExecute() + { + EntityLivingBase entitylivingbase = this.blaze.getAttackTarget(); + return entitylivingbase != null && entitylivingbase.isEntityAlive(); + } + + /** + * Execute a one shot task or start executing a continuous task + */ + public void startExecuting() + { + this.attackStep = 0; + } + + /** + * Resets the task + */ + public void resetTask() + { + // This might be called setOnFire, but what it really controls is whether the wraith is in attack mode. + this.blaze.setOnFire(false); + } + + /** + * Updates the task + */ + public void updateTask() + { + --this.attackTime; + EntityLivingBase entitylivingbase = this.blaze.getAttackTarget(); + double d0 = this.blaze.getDistanceSqToEntity(entitylivingbase); + + if (d0 < 4.0D) + { + if (this.attackTime <= 0) + { + this.attackTime = 20; + this.blaze.attackEntityAsMob(entitylivingbase); + } + + this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ, 1.0D); + } + else if (d0 < 256.0D) + { + if (this.attackTime <= 0) + { + ++this.attackStep; + + if (this.attackStep == 1) + { + this.attackTime = 60; + this.blaze.setOnFire(true); + } + else if (this.attackStep <= 4) + { + this.attackTime = 6; + } + else + { + this.attackTime = 100; + this.attackStep = 0; + this.blaze.setOnFire(false); + } + + if(this.attackStep > 1){ + // Proof, if it were at all needed, of the elegance and versatility of the spell system. + Spells.ice_shard.cast(this.blaze.worldObj, this.blaze, EnumHand.MAIN_HAND, 0, entitylivingbase, new SpellModifiers()); + } + } + + this.blaze.getLookHelper().setLookPositionWithEntity(entitylivingbase, 10.0F, 10.0F); + } + else + { + this.blaze.getNavigator().clearPathEntity(); + this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ, 1.0D); + } + + super.updateTask(); + } + } +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityLightningWraith.java b/src/main/java/electroblob/wizardry/entity/living/EntityLightningWraith.java new file mode 100644 index 00000000..da1cc283 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityLightningWraith.java @@ -0,0 +1,179 @@ +package electroblob.wizardry.entity.living; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.ai.EntityAIBase; +import net.minecraft.entity.ai.EntityAILookIdle; +import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction; +import net.minecraft.entity.ai.EntityAIWander; +import net.minecraft.entity.ai.EntityAIWatchClosest; +import net.minecraft.entity.monster.EntityBlaze; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class EntityLightningWraith extends EntityBlazeMinion { + + public EntityLightningWraith(World world){ + super(world); + } + + public EntityLightningWraith(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world, x, y, z, caster, lifetime); + this.isImmuneToFire = false; + } + + @Override + protected void initEntityAI(){ + super.initEntityAI(); + this.tasks.taskEntries.clear(); + this.tasks.addTask(4, new AILightningAttack(this)); + this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 1.0D)); + this.tasks.addTask(7, new EntityAIWander(this, 1.0D)); + this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F)); + this.tasks.addTask(8, new EntityAILookIdle(this)); + } + + @Override + protected void spawnParticleEffect(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + float brightness = 0.3f + (rand.nextFloat()/2); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, brightness + 0.2f, 1.0f); + } + } + } + + @Override + public void onLivingUpdate(){ + // Fortunately, lightning wraiths don't replace any of blazes' particle effects or the fire sound, they only + // add the sparks, so it's fine to call super here. + if(worldObj.isRemote){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0, 3); + } + super.onLivingUpdate(); + } + + @Override + public boolean attackEntityFrom(DamageSource source, float amount){ + // Removes the damage from being wet that applies to blazes by checking if the mob is actually drowning. + if(source == DamageSource.drown && (this.getAir() > 0 || this.isPotionActive(MobEffects.WATER_BREATHING))){ + // In this case, the lightning wraith is not actually drowning, so cancel the damage. + return false; + }else{ + return super.attackEntityFrom(source, amount); + } + } + + @Override + public boolean isBurning(){ + // Uses the datawatcher on both sides because fire is private to Entity (and I'm not using reflection here). + // TESTME: This should work, but there may be some issues with updating, so if it doesn't work, copy the + // version from Entity and use reflection to access the fire field. + return this.getFlag(0); + } + + /** Copied straight from EntityBlaze.AIFireballAttack, with the only changes being replacement of fireball + * spawning with a one-liner call to WizardryRegistry.arc.cast(...) and the removal of redundant local variables. */ + static class AILightningAttack extends EntityAIBase { + + private final EntityBlaze blaze; + private int attackStep; + private int attackTime; + + public AILightningAttack(EntityBlaze blazeIn) + { + this.blaze = blazeIn; + this.setMutexBits(3); + } + + /** + * Returns whether the EntityAIBase should begin execution. + */ + public boolean shouldExecute() + { + EntityLivingBase entitylivingbase = this.blaze.getAttackTarget(); + return entitylivingbase != null && entitylivingbase.isEntityAlive(); + } + + /** + * Execute a one shot task or start executing a continuous task + */ + public void startExecuting() + { + this.attackStep = 0; + } + + /** + * Resets the task + */ + public void resetTask() + { + // This might be called setOnFire, but what it really controls is whether the wraith is in attack mode. + this.blaze.setOnFire(false); + } + + /** + * Updates the task + */ + public void updateTask() + { + --this.attackTime; + EntityLivingBase entitylivingbase = this.blaze.getAttackTarget(); + double d0 = this.blaze.getDistanceSqToEntity(entitylivingbase); + + if (d0 < 4.0D) + { + if (this.attackTime <= 0) + { + this.attackTime = 20; + this.blaze.attackEntityAsMob(entitylivingbase); + } + + this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ, 1.0D); + } + else if (d0 < 256.0D) + { + if (this.attackTime <= 0) + { + ++this.attackStep; + + if (this.attackStep == 1) + { + this.attackTime = 60; + this.blaze.setOnFire(true); + } + else if (this.attackStep <= 4) + { + this.attackTime = 6; + } + else + { + this.attackTime = 100; + this.attackStep = 0; + this.blaze.setOnFire(false); + } + + if(this.attackStep > 1){ + // Proof, if it were at all needed, of the elegance and versatility of the spell system. + Spells.arc.cast(this.blaze.worldObj, this.blaze, EnumHand.MAIN_HAND, 0, entitylivingbase, new SpellModifiers()); + } + } + + this.blaze.getLookHelper().setLookPositionWithEntity(entitylivingbase, 10.0F, 10.0F); + } + else + { + this.blaze.getNavigator().clearPathEntity(); + this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ, 1.0D); + } + + super.updateTask(); + } + } +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityMagicSlime.java b/src/main/java/electroblob/wizardry/entity/living/EntityMagicSlime.java new file mode 100644 index 00000000..c6799fcf --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityMagicSlime.java @@ -0,0 +1,177 @@ +package electroblob.wizardry.entity.living; + +import java.lang.ref.WeakReference; +import java.util.UUID; + +import javax.annotation.Nullable; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.IEntityLivingData; +import net.minecraft.entity.monster.EntitySlime; +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.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.EnumDifficulty; +import net.minecraft.world.World; + +/** As of Wizardry 1.2, this is now an ISummonedCreature like the rest of them, and it extends EntitySlime. */ +public class EntityMagicSlime extends EntitySlime implements ISummonedCreature { + + // Field implementations + private int lifetime = 200; + private WeakReference casterReference; + private UUID casterUUID; + + // Setter + getter implementations + @Override public int getLifetime(){ return lifetime; } + @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } + @Override public WeakReference getCasterReference(){ return casterReference; } + @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } + @Override public UUID getCasterUUID() { return casterUUID; } + @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } + + public EntityMagicSlime(World world){ + super(world); + this.setSlimeSize(2); // Needs to be called before setting the experience value to 0 + this.experienceValue = 0; + } + + /** + * Creates a new magic slime with the given caster and lifetime, riding the given target. + * @param world The world that the slime is in. + * @param caster The entity that created the slime. + * @param target The slime's victim. The slime will automatically start riding this entity. + * @param lifetime The number of ticks before the slime bursts. + */ + public EntityMagicSlime(World world, EntityLivingBase caster, EntityLivingBase target, int lifetime){ + super(world); + this.setPosition(target.posX, target.posY, target.posZ); + this.startRiding(target); + this.casterReference = new WeakReference(caster); + this.setSlimeSize(2); // Needs to be called before setting the experience value to 0 + this.experienceValue = 0; + this.lifetime = lifetime; + } + + // EntitySlime overrides + + @Override + protected void initEntityAI(){} // Has no AI! + + @Override + protected void dealDamage(EntityLivingBase entity){} // Handles damage itself + + @Override + public void setDead(){ + // Restores behaviour from Entity, replacing slime splitting behaviour. + this.isDead = true; + // Makes sure that the undoing in onUpdate won't undo this. For some reason, EntitySlime sets isDead directly + // to do the peaceful despawning, which seems odd but is actually rather handy! + this.setHealth(0); + // Bursting effect + for(int i=0; i<30; i++){ + double x = this.posX - 0.5 + rand.nextDouble(); + double y = this.posY - 0.5 + rand.nextDouble(); + double z = this.posZ - 0.5 + rand.nextDouble(); + this.worldObj.spawnParticle(EnumParticleTypes.SLIME, x, y, z, (x - this.posX)*2, (y - this.posY)*2, (z - this.posZ)*2); + } + this.playSound(SoundEvents.ENTITY_SLIME_ATTACK, 2.5f, 0.6f); + this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST_FAR, 1.0f, 0.5f); + } + + @Override + public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata){ + // Removes size randomisation + IEntityLivingData data = super.onInitialSpawn(difficulty, livingdata); + this.setSlimeSize(2); + return data; + } + + @Override + public boolean attackEntityFrom(DamageSource source, float amount){ + // Immune to suffocation + return source == DamageSource.inWall ? false : super.attackEntityFrom(source, amount); + } + + // Implementations + + @Override + public void setRevengeTarget(EntityLivingBase entity){ + if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); + } + + @Override + public void onUpdate(){ + + super.onUpdate(); + // Undoes the despawning on peaceful behaviour. I don't think there's anything in super.onUpdate that sets + // isDead other than that, but it's better to do a quick sanity check just to be sure. + if(this.isDead && worldObj.getDifficulty() == EnumDifficulty.PEACEFUL && this.getHealth() > 0) this.isDead = false; + // Bursts instantly rather than doing the falling over animation. + if(this.getHealth() <= 0) this.setDead(); + + this.updateDelegate(); + + // Damages and slows the slime's victim or makes the slime explode if the victim is dead. + if(this.getRidingEntity() != null && this.getRidingEntity() instanceof EntityLivingBase && ((EntityLivingBase)this.getRidingEntity()).getHealth() > 0){ + if(this.ticksExisted % 16 == 1){ + this.getRidingEntity().attackEntityFrom(DamageSource.magic, 1); + ((EntityLivingBase)this.getRidingEntity()).addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, 20, 2)); + this.playSound(SoundEvents.ENTITY_SLIME_ATTACK, 1.0f, 1.0f); + this.squishAmount = 0.5F; + } + }else{ + this.setDead(); + } + } + + @Override + public void onSpawn(){} + + @Override + public void onDespawn(){} + + @Override + public boolean hasParticleEffect() { + return false; + } + + @Override + protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { + // In this case, the delegate method determines whether super is called. + // Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements. + return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + this.writeNBTDelegate(nbttagcompound); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + this.writeNBTDelegate(nbttagcompound); + } + + // Recommended overrides + + @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } + @Override protected boolean canDropLoot(){ return false; } + @Override protected Item getDropItem(){ return null; } + @Override protected ResourceLocation getLootTable(){ return null; } + @Override public boolean canPickUpLoot(){ return false; } + // This vanilla method has nothing to do with the custom onDespawn() method. + @Override protected boolean canDespawn(){ return false; } + +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityPhoenix.java b/src/main/java/electroblob/wizardry/entity/living/EntityPhoenix.java new file mode 100644 index 00000000..5d72f26e --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityPhoenix.java @@ -0,0 +1,189 @@ +package electroblob.wizardry.entity.living; + +import java.util.Collections; +import java.util.List; + +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAILookIdle; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; +import net.minecraft.entity.ai.EntityAIWatchClosest; +import net.minecraft.init.SoundEvents; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.SoundEvent; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class EntityPhoenix extends EntitySummonedCreature implements ISpellCaster { + + private double AISpeed = 0.5; + + // Can attack for 7 seconds, then must cool down for 3. + private EntityAIAttackSpell spellAttackAI = new EntityAIAttackSpell(this, AISpeed, 15f, 60, 140); + + private Spell continuousSpell; + + private static final List attack = Collections.singletonList(Spells.flame_ray); + + public EntityPhoenix(World world){ + super(world); + } + + public EntityPhoenix(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world, x, y, z, caster, lifetime); + this.isImmuneToFire = true; + this.height = 2.0f; + // For some reason this can't be in initEntityAI + this.tasks.addTask(1, this.spellAttackAI); + } + + @Override + protected void initEntityAI(){ + + this.tasks.addTask(0, new EntityAIWatchClosest(this, EntityLivingBase.class, 0)); + //this.tasks.addTask(2, new EntityAIWander(this, AISpeed)); + this.tasks.addTask(3, new EntityAILookIdle(this)); + //this.targetTasks.addTask(0, new EntityAIMoveTowardsTarget(this, 1, 10)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, + 0, false, true, this.getTargetSelector())); + + this.setAIMoveSpeed((float)AISpeed); + } + + @Override + public List getSpells(){ + return attack; + } + + @Override + public SpellModifiers getModifiers(){ + return new SpellModifiers(); + } + + @Override + public Spell getContinuousSpell(){ + return continuousSpell; + } + + @Override + public void setContinuousSpell(Spell spell){ + continuousSpell = spell; + } + + @Override + public boolean hasRangedAttack() { + return true; + } + + @Override + // Makes the flames come from the phoenix's head rather than its body + public float getEyeHeight(){ + return 2.1f; + } + + @Override + protected void applyEntityAttributes(){ + super.applyEntityAttributes(); + this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D); + this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(30.0D); + this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30.0D); + } + + @Override + protected SoundEvent getAmbientSound(){ + return SoundEvents.ENTITY_BLAZE_AMBIENT; + } + + @Override + protected SoundEvent getHurtSound(){ + return SoundEvents.ENTITY_BLAZE_HURT; + } + + @Override + protected SoundEvent getDeathSound(){ + return SoundEvents.ENTITY_BLAZE_DEATH; + } + + @Override + @SideOnly(Side.CLIENT) + public int getBrightnessForRender(float par1){ + return 15728880; + } + + @Override + public float getBrightness(float par1){ + return 1.0F; + } + + @Override + public void onSpawn(){ + this.spawnParticleEffect(); + } + + @Override + public void onDespawn(){ + this.spawnParticleEffect(); + } + + private void spawnParticleEffect(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + this.worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); + } + } + } + + @Override + public void onLivingUpdate(){ + + // Makes the phoenix hover. + int floorLevel = WizardryUtilities.getNearestFloorLevel(worldObj, new BlockPos(this), 4); + + if(this.posY - floorLevel > 3){ + this.motionY = -0.1; + }else if(this.posY - floorLevel < 2){ + this.motionY = 0.1; + }else{ + this.motionY = 0.0; + } + + // Living sound + if(this.rand.nextInt(24) == 0){ + this.playSound(SoundEvents.BLOCK_FIRE_AMBIENT, 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F); + } + + // Flapping sound effect + if(this.ticksExisted % 22 == 0){ + this.playSound(SoundEvents.ENTITY_ENDERDRAGON_FLAP, 1.0F, 1.0f); + } + + for(int i=0; i<2; i++){ + this.worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.height/2 + this.rand.nextDouble() * (double)this.height/2, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0D, -0.1D, 0.0D); + } + + // Adding this allows the phoenix to attack despite being in the air. However, for some strange reason + // it will only attack when within about 3 blocks of the ground. Any higher and it just sits there, not even + // attempting to find targets. + + this.onGround = true; + + super.onLivingUpdate(); + } + + @Override + public void fall(float distance, float damageMultiplier){} // Immune to fall damage + + @Override + public boolean isBurning() + { + return false; + } +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityShadowWraith.java b/src/main/java/electroblob/wizardry/entity/living/EntityShadowWraith.java new file mode 100644 index 00000000..000ba391 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityShadowWraith.java @@ -0,0 +1,165 @@ +package electroblob.wizardry.entity.living; + +import java.util.Collections; +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIAttackMelee; +import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAILookIdle; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; +import net.minecraft.entity.ai.EntityAIWander; +import net.minecraft.init.MobEffects; +import net.minecraft.init.SoundEvents; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.SoundEvent; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class EntityShadowWraith extends EntitySummonedCreature implements ISpellCaster { + + // TODO: This currently doesn't fly like it used to. Should it, or does it not matter? + + private double AISpeed = 1.0; + + private EntityAIAttackSpell spellAttackAI = new EntityAIAttackSpell(this, AISpeed, 15f, 30, 0); + + private static final List attack = Collections.singletonList(Spells.darkness_orb); + + public EntityShadowWraith(World world){ + super(world); + } + + public EntityShadowWraith(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world, x, y, z, caster, lifetime); + // For some reason this can't be in initEntityAI + this.tasks.addTask(0, this.spellAttackAI); + } + + @Override + protected void initEntityAI(){ + + this.tasks.addTask(1, new EntityAIAttackMelee(this, AISpeed, false)); + this.tasks.addTask(2, new EntityAIWander(this, AISpeed)); + this.tasks.addTask(3, new EntityAILookIdle(this)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, + 0, false, true, this.getTargetSelector())); + + this.setAIMoveSpeed((float)AISpeed); + } + + @Override + public boolean hasRangedAttack() { + return true; + } + + @Override + public List getSpells(){ + return attack; + } + + @Override + public SpellModifiers getModifiers(){ + return new SpellModifiers(); + } + + @Override + public Spell getContinuousSpell(){ + return Spells.none; + } + + @Override + public void setContinuousSpell(Spell spell){ + // Doesn't use continuous spells. + } + + @Override + protected void applyEntityAttributes(){ + super.applyEntityAttributes(); + this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D); + this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(AISpeed); + this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30.0D); + this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(16.0D); + } + + @Override + public boolean isPotionApplicable(PotionEffect potion){ + return potion.getPotion() == MobEffects.WITHER ? false : super.isPotionApplicable(potion); + } + + @Override + protected SoundEvent getAmbientSound(){ + return SoundEvents.ENTITY_BLAZE_AMBIENT; + } + + @Override + protected SoundEvent getHurtSound(){ + return SoundEvents.ENTITY_BLAZE_HURT; + } + + @Override + protected SoundEvent getDeathSound(){ + return SoundEvents.ENTITY_BLAZE_DEATH; + } + + @Override + @SideOnly(Side.CLIENT) + public int getBrightnessForRender(float partialTicks){ + return 15728880; + } + + @Override + public float getBrightness(float partialTicks){ + return 1.0F; + } + + @Override + public void onSpawn(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + float brightness = rand.nextFloat()*0.4f; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, 0.0f, brightness); + } + } + } + + @Override + public void onLivingUpdate(){ + + if(this.rand.nextInt(24) == 0){ + this.playSound(SoundEvents.BLOCK_PORTAL_AMBIENT, 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F); + } + + // Slow fall + if(!this.onGround && this.motionY < 0.0D){ + this.motionY *= 0.6D; + } + + if(worldObj.isRemote){ + for(int i=0; i<2; i++){ + worldObj.spawnParticle(EnumParticleTypes.PORTAL, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0); + worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0); + float brightness = rand.nextFloat()*0.2f; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, 0.0f, brightness); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + } + } + + super.onLivingUpdate(); + } + + @Override + public void fall(float distance, float damageMultiplier){ + // Immune to fall damage. + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntitySilverfishMinion.java b/src/main/java/electroblob/wizardry/entity/living/EntitySilverfishMinion.java new file mode 100644 index 00000000..2a8f237e --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntitySilverfishMinion.java @@ -0,0 +1,183 @@ +package electroblob.wizardry.entity.living; + +import java.lang.ref.WeakReference; +import java.util.UUID; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.EntityFlying; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.ai.EntityAIAttackMelee; +import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; +import net.minecraft.entity.ai.EntityAISwimming; +import net.minecraft.entity.monster.EntitySilverfish; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumHand; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +public class EntitySilverfishMinion extends EntitySilverfish implements ISummonedCreature { + + // Field implementations + private int lifetime = 600; + private WeakReference casterReference; + private UUID casterUUID; + + // Setter + getter implementations + @Override public int getLifetime(){ return lifetime; } + @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } + @Override public WeakReference getCasterReference(){ return casterReference; } + @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } + @Override public UUID getCasterUUID() { return casterUUID; } + @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } + + /** + * Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't + * matter because the client side entity immediately gets the lifetime value copied over to it by this class + * anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's + * no need to do anything inside it other than call super(). + */ + public EntitySilverfishMinion(World world){ + super(world); + this.experienceValue = 0; + } + + /** + * Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when + * extending this class (be sure to call super()) so that AI and other things can be added. + */ + public EntitySilverfishMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world); + this.setPosition(x, y, z); + this.casterReference = new WeakReference(caster); + this.experienceValue = 0; + this.lifetime = lifetime; + } + + // EntitySilverfish overrides + @Override + protected void initEntityAI() + { + // Super not called because we don't want AISummonSilverfish or AIHideInStone + this.tasks.addTask(1, new EntityAISwimming(this)); + this.tasks.addTask(4, new EntityAIAttackMelee(this, 1.0D, false)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, + 0, false, true, this.getTargetSelector())); + } + + // Implementations + + @Override + public void setRevengeTarget(EntityLivingBase entity){ + if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); + } + + @Override + public void onUpdate(){ + super.onUpdate(); + this.updateDelegate(); + } + + @Override + public void onSpawn(){ + this.spawnParticleEffect(); + } + + @Override + public void onDespawn(){ + this.spawnParticleEffect(); + } + + private void spawnParticleEffect(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + this.rand.nextFloat(), this.posY + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0.0d, 0.0d, 0.0d, 0, 0.3f, 0.3f, 0.3f); + } + } + } + + @Override + public void onSuccessfulAttack(EntityLivingBase target){ + if(!target.isEntityAlive()){ + this.onKillEntity(target); + } + } + + @Override + public void onKillEntity(EntityLivingBase victim) { + // If the silverfish has a summoner, this is actually called from Wizardry's event handler rather than by + // Minecraft itself, because the damagesource being changed causes it not to get called. + if(!this.worldObj.isRemote){ + // Summons 1-4 more silverfish + int alliesToSummon = rand.nextInt(4) + 1; + + for(int i=0; i entityType){ + // Returns true unless the given entity type is a flying entity. + return !EntityFlying.class.isAssignableFrom(entityType); + } + + @Override + public ITextComponent getDisplayName(){ + if(getCaster() != null){ + return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(), + new TextComponentTranslation("entity." + this.getEntityString() + ".name")); + }else{ + return super.getDisplayName(); + } + } + + @Override + public boolean hasCustomName(){ + // If this returns true, the renderer will show the nameplate when looking directly at the entity + return Wizardry.settings.showSummonedCreatureNames && getCaster() != null; + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/entity/living/EntitySkeletonMinion.java b/src/main/java/electroblob/wizardry/entity/living/EntitySkeletonMinion.java new file mode 100644 index 00000000..e62435f3 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntitySkeletonMinion.java @@ -0,0 +1,216 @@ +package electroblob.wizardry.entity.living; + +import java.lang.ref.WeakReference; +import java.util.Calendar; +import java.util.UUID; + +import javax.annotation.Nullable; + +import electroblob.wizardry.Wizardry; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.IEntityLivingData; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; +import net.minecraft.entity.ai.attributes.AttributeModifier; +import net.minecraft.entity.monster.EntitySkeleton; +import net.minecraft.entity.monster.SkeletonType; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.init.MobEffects; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.World; + +public class EntitySkeletonMinion extends EntitySkeleton implements ISummonedCreature { + + // Field implementations + private int lifetime = 600; + private WeakReference casterReference; + private UUID casterUUID; + + // Setter + getter implementations + @Override public int getLifetime(){ return lifetime; } + @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } + @Override public WeakReference getCasterReference(){ return casterReference; } + @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } + @Override public UUID getCasterUUID() { return casterUUID; } + @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } + + /** + * Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't + * matter because the client side entity immediately gets the lifetime value copied over to it by this class + * anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's + * no need to do anything inside it other than call super(). + */ + public EntitySkeletonMinion(World world){ + super(world); + this.experienceValue = 0; + } + + /** + * Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when + * extending this class (be sure to call super()) so that AI and other things can be added. + */ + public EntitySkeletonMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world); + this.setPosition(x, y, z); + this.casterReference = new WeakReference(caster); + this.experienceValue = 0; + this.lifetime = lifetime; + } + + // EntitySkeleton overrides + + // This particular override is pretty standard: let the superclass handle basic AI like swimming, but replace its + // targeting system with one that targets hostile mobs and takes the ADS into account. + @Override + protected void initEntityAI() + { + super.initEntityAI(); + this.targetTasks.taskEntries.clear(); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, + 0, false, true, this.getTargetSelector())); + } + + // Shouldn't have randomised armour, but does still need a bow! + @Override + protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty) { + this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); + } + + // Where the skeleton minion is summoned does not affect its type. + @Override + public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata) + { + // Can't call super, so the code from the next level up (EntityLiving) had to be copied as well. + this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).applyModifier(new AttributeModifier("Random spawn bonus", this.rand.nextGaussian() * 0.05D, 1)); + + if (this.rand.nextFloat() < 0.05F) + { + this.setLeftHanded(true); + } + else + { + this.setLeftHanded(false); + } + + // Halloween pumpkin heads! Why not? + if (this.getItemStackFromSlot(EntityEquipmentSlot.HEAD) == null) + { + Calendar calendar = this.worldObj.getCurrentDate(); + + if (calendar.get(2) + 1 == 10 && calendar.get(5) == 31 && this.rand.nextFloat() < 0.25F) + { + this.setItemStackToSlot(EntityEquipmentSlot.HEAD, new ItemStack(this.rand.nextFloat() < 0.1F ? Blocks.LIT_PUMPKIN : Blocks.PUMPKIN)); + this.inventoryArmorDropChances[EntityEquipmentSlot.HEAD.getIndex()] = 0.0F; + } + } + + return livingdata; + } + + // Implementations + + @Override + public void setRevengeTarget(EntityLivingBase entity){ + if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); + } + + @Override + public void onUpdate(){ + super.onUpdate(); + this.updateDelegate(); + } + + @Override + public void onSpawn(){ + this.spawnParticleEffect(); + } + + @Override + public void onDespawn(){ + this.spawnParticleEffect(); + } + + private void spawnParticleEffect(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + this.worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); + } + } + } + + @Override + public boolean hasParticleEffect() { + return true; + } + + @Override + public void onSuccessfulAttack(EntityLivingBase target) { + if(this.getSkeletonType() == SkeletonType.WITHER){ + target.addPotionEffect(new PotionEffect(MobEffects.WITHER, 200)); + } + } + + @Override + protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { + // In this case, the delegate method determines whether super is called. + // Rather handily, we can make use of Java's 'stop as soon as you find true' method of evaluating OR statements. + return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + this.writeNBTDelegate(nbttagcompound); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + this.writeNBTDelegate(nbttagcompound); + } + + // Recommended overrides + + @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } + @Override protected boolean canDropLoot(){ return false; } + @Override protected Item getDropItem(){ return null; } + @Override protected ResourceLocation getLootTable(){ return null; } + @Override public boolean canPickUpLoot(){ return false; } + // This vanilla method has nothing to do with the custom despawn() method. + @Override protected boolean canDespawn(){ return false; } + + @Override + public boolean canAttackClass(Class entityType){ + return true; + } + + @Override + public ITextComponent getDisplayName(){ + if(getCaster() != null){ + return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(), + new TextComponentTranslation("entity." + this.getEntityString() + ".name")); + }else{ + return super.getDisplayName(); + } + } + + @Override + public boolean hasCustomName(){ + // If this returns true, the renderer will show the nameplate when looking directly at the entity + return Wizardry.settings.showSummonedCreatureNames && getCaster() != null; + } +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntitySpiderMinion.java b/src/main/java/electroblob/wizardry/entity/living/EntitySpiderMinion.java new file mode 100644 index 00000000..f5714361 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntitySpiderMinion.java @@ -0,0 +1,207 @@ +package electroblob.wizardry.entity.living; + +import java.lang.ref.WeakReference; +import java.util.UUID; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.EntityFlying; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.IEntityLivingData; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; +import net.minecraft.entity.ai.attributes.AttributeModifier; +import net.minecraft.entity.monster.EntityCaveSpider; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.EnumDifficulty; +import net.minecraft.world.World; + +public class EntitySpiderMinion extends EntityCaveSpider implements ISummonedCreature { + + // Field implementations + private int lifetime = 600; + private WeakReference casterReference; + private UUID casterUUID; + + // Setter + getter implementations + @Override public int getLifetime(){ return lifetime; } + @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } + @Override public WeakReference getCasterReference(){ return casterReference; } + @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } + @Override public UUID getCasterUUID() { return casterUUID; } + @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } + + /** + * Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't + * matter because the client side entity immediately gets the lifetime value copied over to it by this class + * anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's + * no need to do anything inside it other than call super(). + */ + public EntitySpiderMinion(World world){ + super(world); + this.experienceValue = 0; + } + + /** + * Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when + * extending this class (be sure to call super()) so that AI and other things can be added. + */ + public EntitySpiderMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world); + this.setPosition(x, y, z); + this.casterReference = new WeakReference(caster); + this.experienceValue = 0; + this.lifetime = lifetime; + } + + // EntitySpider overrides + + // This particular override is pretty standard: let the superclass handle basic AI like swimming, but replace its + // targeting system with one that targets hostile mobs and takes the ADS into account. + @Override + protected void initEntityAI() + { + super.initEntityAI(); + this.targetTasks.taskEntries.clear(); + // Spiders use a custom AI type specific to spiders which I can't access, but it's just an extension of + // EntityAINearestAttackableTarget which takes daylight into account. Since I want spider minions to attack + // regardless of daylight, I can just use EntityAINearestAttackableTarget. + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, + 0, false, true, this.getTargetSelector())); + } + + // No spider jockeys! + @Override + public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData livingdata) { + + // Can't call super, so the code from the next level up (EntityLiving) had to be copied as well. + this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).applyModifier(new AttributeModifier("Random spawn bonus", this.rand.nextGaussian() * 0.05D, 1)); + + if (this.rand.nextFloat() < 0.05F) + { + this.setLeftHanded(true); + } + else + { + this.setLeftHanded(false); + } + + // Don't need anything from EntitySpider, since neither spider jockeys nor group data is relevant. + return livingdata; + } + + // Implementations + + @Override + public void setRevengeTarget(EntityLivingBase entity){ + if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); + } + + @Override + public void onUpdate(){ + super.onUpdate(); + this.updateDelegate(); + } + + @Override + public void onSpawn(){ + this.spawnParticleEffect(); + } + + @Override + public void onDespawn(){ + this.spawnParticleEffect(); + } + + private void spawnParticleEffect(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + this.rand.nextFloat(), this.posY + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.2f, 0.0f); + } + } + } + + @Override + public boolean hasParticleEffect() { + return true; + } + + @Override + public void onSuccessfulAttack(EntityLivingBase target){ + + int seconds = 0; + + if(this.worldObj.getDifficulty() == EnumDifficulty.NORMAL){ + seconds = 7; + }else if(this.worldObj.getDifficulty() == EnumDifficulty.HARD){ + seconds = 15; + } + + if(seconds > 0){ + target.addPotionEffect(new PotionEffect(MobEffects.POISON, seconds * 20, 0)); + } + } + + @Override + protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { + // In this case, the delegate method determines whether super is called. + // Rather handily, we can make use of Java's 'stop as soon as you find true' method of evaluating OR statements. + return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + this.writeNBTDelegate(nbttagcompound); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + this.writeNBTDelegate(nbttagcompound); + } + + // Recommended overrides + + @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } + @Override protected boolean canDropLoot(){ return false; } + @Override protected Item getDropItem(){ return null; } + @Override protected ResourceLocation getLootTable(){ return null; } + @Override public boolean canPickUpLoot(){ return false; } + // This vanilla method has nothing to do with the custom despawn() method. + @Override protected boolean canDespawn(){ return false; } + + @Override + public boolean canAttackClass(Class entityType){ + // Returns true unless the given entity type is a flying entity. + return !EntityFlying.class.isAssignableFrom(entityType); + } + + @Override + public ITextComponent getDisplayName(){ + if(getCaster() != null){ + return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(), + new TextComponentTranslation("entity." + this.getEntityString() + ".name")); + }else{ + return super.getDisplayName(); + } + } + + @Override + public boolean hasCustomName(){ + // If this returns true, the renderer will show the nameplate when looking directly at the entity + return Wizardry.settings.showSummonedCreatureNames && getCaster() != null; + } +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntitySpiritHorse.java b/src/main/java/electroblob/wizardry/entity/living/EntitySpiritHorse.java new file mode 100644 index 00000000..47be717e --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntitySpiritHorse.java @@ -0,0 +1,203 @@ +package electroblob.wizardry.entity.living; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.IEntityLivingData; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.passive.EntityAnimal; +import net.minecraft.entity.passive.EntityHorse; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumHand; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.text.translation.I18n; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.World; + +/** Does not implement ISummonedCreature because it has different despawning rules and because EntityHorse already + * has an owner system. */ +@SuppressWarnings("deprecation") // It's what Entity does, so... +public class EntitySpiritHorse extends EntityHorse { + + private int idleTimer = 0; + + public EntitySpiritHorse(World par1World) + { + super(par1World); + } + + @Override + public String getName() + { + if (this.hasCustomName()) + { + return this.getCustomNameTag(); + } + else + { + return I18n.translateToLocal("entity.wizardry.Spirit Horse.name"); + } + } + + @Override + public boolean isChested() + { + return false; + } + + @Override + public int getTotalArmorValue() + { + return 0; + } + + @Override + protected int getExperiencePoints(EntityPlayer p_70693_1_){ + return 0; + } + + @Override + protected Item getDropItem(){ + + return null; + } + + @Override + protected void dropFewItems(boolean par1, int par2){} + + @Override + protected void applyEntityAttributes() + { + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(24.0D); + } + + @Override + public void openGUI(EntityPlayer p_110199_1_){} + + @Override + public boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack){ + + ItemStack itemstack = player.inventory.getCurrentItem(); + + // Allows the owner (but not other players) to dispel the spirit horse using a wand (shift-clicking, because clicking mounts the horse in this case). + if(itemstack != null && itemstack.getItem() instanceof ItemWand && this.getOwner() == player && player.isSneaking()){ + // Prevents accidental double clicking. + if(this.ticksExisted > 20){ + for(int i=0;i<15;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); + } + this.setDead(); + if(WizardData.get(player) != null){ + WizardData.get(player).hasSpiritHorse = false; + } + this.playSound(WizardrySounds.SPELL_HEAL, 0.7F, rand.nextFloat() * 0.4F + 1.0F); + // This is necessary to prevent the wand's spell being cast when performing this action. + return true; + } + return false; + } + + return super.processInteract(player, hand, itemstack); + } + + @Override + public void onDeath(DamageSource par1DamageSource){ + + super.onDeath(par1DamageSource); + + // Allows player to summon another spirit horse once this one has died. + if(this.getOwner() instanceof EntityPlayer && WizardData.get((EntityPlayer)this.getOwner()) != null){ + WizardData.get((EntityPlayer)this.getOwner()).hasSpiritHorse = false; + } + } + + // I wrote this one! + private EntityLivingBase getOwner(){ + + // I think the DataManager stores any objects, so it now stores the UUID instead of its string representation. + Entity owner = WizardryUtilities.getEntityByUUID(worldObj, this.getOwnerUniqueId()); + + if(owner instanceof EntityLivingBase){ + return (EntityLivingBase)owner; + }else{ + return null; + } + } + + @Override + public void onUpdate(){ + + super.onUpdate(); + + // Adds a dust particle effect + if(this.worldObj.isRemote){ + Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 0, 0.8f, 0.8f, 1.0f); + } + + // Spirit horse disappears a short time after being dismounted. + if(!this.isBeingRidden()){ + this.idleTimer++; + }else if(this.idleTimer > 0){ + this.idleTimer = 0; + } + + if(this.idleTimer > 200){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); + } + } + this.playSound(WizardrySounds.SPELL_HEAL, 0.7F, rand.nextFloat() * 0.4F + 1.0F); + // Allows player to summon another spirit horse once this one has disappeared. + if(this.getOwner() instanceof EntityPlayer && WizardData.get((EntityPlayer)this.getOwner()) != null){ + WizardData.get((EntityPlayer)this.getOwner()).hasSpiritHorse = false; + } + this.setDead(); + } + } + + @Override + public boolean canMateWith(EntityAnimal par1EntityAnimal){ + return false; + } + + @Override + public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData data){ + + // Adds Particles on spawn. Due to client/server differences this cannot be done in the item. + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); + } + } + + return super.onInitialSpawn(difficulty, data); + } + + @Override + public ITextComponent getDisplayName(){ + if(getOwner() != null){ + return new TextComponentTranslation(ISummonedCreature.NAMEPLATE_TRANSLATION_KEY, getOwner().getName(), + new TextComponentTranslation("entity." + this.getEntityString() + ".name")); + }else{ + return super.getDisplayName(); + } + } + + @Override + public boolean hasCustomName(){ + // If this returns true, the renderer will show the nameplate when looking directly at the entity + return Wizardry.settings.showSummonedCreatureNames && getOwner() != null; + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntitySpiritWolf.java b/src/main/java/electroblob/wizardry/entity/living/EntitySpiritWolf.java new file mode 100644 index 00000000..6de76c7b --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntitySpiritWolf.java @@ -0,0 +1,157 @@ +package electroblob.wizardry.entity.living; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.EntityAgeable; +import net.minecraft.entity.IEntityLivingData; +import net.minecraft.entity.ai.EntityAIAttackMelee; +import net.minecraft.entity.ai.EntityAIFollowOwner; +import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAILeapAtTarget; +import net.minecraft.entity.ai.EntityAILookIdle; +import net.minecraft.entity.ai.EntityAIOwnerHurtByTarget; +import net.minecraft.entity.ai.EntityAIOwnerHurtTarget; +import net.minecraft.entity.ai.EntityAISit; +import net.minecraft.entity.ai.EntityAISwimming; +import net.minecraft.entity.ai.EntityAIWander; +import net.minecraft.entity.ai.EntityAIWatchClosest; +import net.minecraft.entity.passive.EntityWolf; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumHand; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.World; + +/** Does not implement ISummonedCreature because it has different despawning rules and because EntityWolf already + * has an owner system. */ +public class EntitySpiritWolf extends EntityWolf { + + public EntitySpiritWolf(World par1World){ + + super(par1World); + this.experienceValue = 0; + } + + @Override + protected void initEntityAI(){ + + this.aiSit = new EntityAISit(this); + this.tasks.addTask(1, new EntityAISwimming(this)); + this.tasks.addTask(2, this.aiSit); + this.tasks.addTask(3, new EntityAILeapAtTarget(this, 0.4F)); + this.tasks.addTask(4, new EntityAIAttackMelee(this, 1.0D, true)); + this.tasks.addTask(5, new EntityAIFollowOwner(this, 1.0D, 10.0F, 2.0F)); + this.tasks.addTask(7, new EntityAIWander(this, 1.0D)); + this.tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F)); + this.tasks.addTask(9, new EntityAILookIdle(this)); + this.targetTasks.addTask(1, new EntityAIOwnerHurtByTarget(this)); + this.targetTasks.addTask(2, new EntityAIOwnerHurtTarget(this)); + this.targetTasks.addTask(3, new EntityAIHurtByTarget(this, true, new Class[0])); + } + + @Override + public void onDeath(DamageSource source){ + + // Allows player to summon another spirit wolf once this one has died. + // NOTE: This has been known to work incorrectly. + if(this.getOwner() instanceof EntityPlayer && WizardData.get((EntityPlayer)this.getOwner()) != null){ + WizardData.get((EntityPlayer)this.getOwner()).hasSpiritWolf = false; + } + + super.onDeath(source); + } + + @Override + protected int getExperiencePoints(EntityPlayer p_70693_1_){ + return 0; + } + + @Override + public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData livingdata) { + + // Adds Particles on spawn. Due to client/server differences this cannot be done in the item. + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); + } + } + + return livingdata; + } + + @Override + public void onUpdate(){ + + super.onUpdate(); + + // Adds a dust particle effect + if(this.worldObj.isRemote){ + Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 0, 0.8f, 0.8f, 1.0f); + } + } + + @Override + public boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { + + if (this.isTamed()) + { + if (stack != null){ + + // Allows the owner (but not other players) to dispel the spirit wolf using a wand. + if(stack != null && stack.getItem() instanceof ItemWand && this.getOwner() == player && player.isSneaking()){ + // Prevents accidental double clicking. + if(this.ticksExisted > 20){ + for(int i=0;i<10;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); + } + this.setDead(); + if(WizardData.get(player) != null){ + WizardData.get(player).hasSpiritWolf = false; + } + this.playSound(WizardrySounds.SPELL_HEAL, 0.7F, rand.nextFloat() * 0.4F + 1.0F); + // This is necessary to prevent the wand's spell being cast when performing this action. + return true; + } + } + } + } + + return super.processInteract(player, hand, stack); + } + + @Override + public EntityWolf createChild(EntityAgeable par1EntityAgeable) + { + return null; + } + + @Override + protected Item getDropItem() + { + return null; + } + + @Override + public ITextComponent getDisplayName(){ + if(getOwner() != null){ + return new TextComponentTranslation(ISummonedCreature.NAMEPLATE_TRANSLATION_KEY, getOwner().getName(), + new TextComponentTranslation("entity." + this.getEntityString() + ".name")); + }else{ + return super.getDisplayName(); + } + } + + @Override + public boolean hasCustomName(){ + // If this returns true, the renderer will show the nameplate when looking directly at the entity + return Wizardry.settings.showSummonedCreatureNames && getOwner() != null; + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityStormElemental.java b/src/main/java/electroblob/wizardry/entity/living/EntityStormElemental.java new file mode 100644 index 00000000..204d95a0 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityStormElemental.java @@ -0,0 +1,160 @@ +package electroblob.wizardry.entity.living; + +import java.util.Collections; +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIAttackMelee; +import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAILookIdle; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; +import net.minecraft.entity.ai.EntityAIWander; +import net.minecraft.entity.effect.EntityLightningBolt; +import net.minecraft.init.SoundEvents; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.SoundEvent; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class EntityStormElemental extends EntitySummonedCreature implements ISpellCaster { + + private double AISpeed = 1.0; + + private EntityAIAttackSpell spellAttackAI = new EntityAIAttackSpell(this, AISpeed, 15f, 30, 0); + + private static final List attack = Collections.singletonList(Spells.lightning_disc); + + public EntityStormElemental(World world){ + super(world); + } + + public EntityStormElemental(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world, x, y, z, caster, lifetime); + // For some reason this can't be in initEntityAI + this.tasks.addTask(0, this.spellAttackAI); + } + + @Override + protected void initEntityAI(){ + + this.tasks.addTask(1, new EntityAIAttackMelee(this, AISpeed, false)); + this.tasks.addTask(2, new EntityAIWander(this, AISpeed)); + this.tasks.addTask(3, new EntityAILookIdle(this)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, + 0, false, true, this.getTargetSelector())); + + this.setAIMoveSpeed((float)AISpeed); + } + + @Override + public boolean hasRangedAttack() { + return true; + } + + @Override + public List getSpells(){ + return attack; + } + + @Override + public SpellModifiers getModifiers(){ + return new SpellModifiers(); + } + + @Override + public Spell getContinuousSpell(){ + return Spells.none; + } + + @Override + public void setContinuousSpell(Spell spell){ + // Doesn't use continuous spells. + } + + @Override + protected void applyEntityAttributes(){ + super.applyEntityAttributes(); + this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D); + this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(AISpeed); + this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30.0D); + this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(16.0D); + } + + @Override + protected SoundEvent getAmbientSound(){ + return SoundEvents.ENTITY_BLAZE_AMBIENT; + } + + @Override + protected SoundEvent getHurtSound(){ + return SoundEvents.ENTITY_BLAZE_HURT; + } + + @Override + protected SoundEvent getDeathSound(){ + return SoundEvents.ENTITY_BLAZE_DEATH; + } + + @Override + @SideOnly(Side.CLIENT) + public int getBrightnessForRender(float partialTicks){ + return 15728880; + } + + @Override + public float getBrightness(float partialTicks){ + return 1.0F; + } + + @Override + public void onLivingUpdate(){ + + if(this.ticksExisted % 120 == 1){ + this.playSound(WizardrySounds.SPELL_LOOP_WIND, 1.0f, 1.0f); + } + + if (this.rand.nextInt(24) == 0){ + this.playSound(SoundEvents.ENTITY_BLAZE_BURN, 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F); + } + + // Slow fall + if(!this.onGround && this.motionY < 0.0D){ + this.motionY *= 0.6D; + } + + if(worldObj.isRemote){ + + for(int i=0; i<2; ++i){ + worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0, 0, 0); + } + + for(int i=0; i<10; i++){ + float brightness = rand.nextFloat()*0.2f; + double dy = this.rand.nextDouble() * (double)this.height; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE_ROTATING, worldObj, this.posX, this.posY + dy, this.posZ, 0, 0, 0, 20 + rand.nextInt(10), 0, brightness, brightness, false, 0.2f + 0.5f*dy); + } + } + + super.onLivingUpdate(); + } + + @Override + public void fall(float distance, float damageMultiplier){ + // Immune to fall damage. + } + + @Override + public void onStruckByLightning(EntityLightningBolt lightning){ + // Immune to lightning. + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/entity/living/EntitySummonedCreature.java b/src/main/java/electroblob/wizardry/entity/living/EntitySummonedCreature.java new file mode 100644 index 00000000..9ed6d48f --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntitySummonedCreature.java @@ -0,0 +1,144 @@ +package electroblob.wizardry.entity.living; + +import java.lang.ref.WeakReference; +import java.util.UUID; + +import electroblob.wizardry.Wizardry; +import net.minecraft.entity.EntityCreature; +import net.minecraft.entity.EntityFlying; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumHand; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +/** Abstract base implementation of {@link ISummonedCreature} which is the superclass to all custom summoned entities + * (i.e. entities that don't extend vanilla/mod creatures). Also serves as an example of how to correctly implement + * the above interface, and includes some non-critical method overrides which should be used for best results (xp, drops, + * and such like). Not to be confused with the old version of EntitySummonedCreature; that system has been replaced. + * @since Wizardry 1.2 + * @author Electroblob */ +public abstract class EntitySummonedCreature extends EntityCreature implements ISummonedCreature { + + // Field implementations + private int lifetime = 600; + private WeakReference casterReference; + private UUID casterUUID; + + // Setter + getter implementations + @Override public int getLifetime(){ return lifetime; } + @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } + @Override public WeakReference getCasterReference(){ return casterReference; } + @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } + @Override public UUID getCasterUUID() { return casterUUID; } + @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } + + /** + * Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't + * matter because the client side entity immediately gets the lifetime value copied over to it by this class + * anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's + * no need to do anything inside it other than call super(). + */ + public EntitySummonedCreature(World world){ + super(world); + this.experienceValue = 0; + } + + /** + * Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when + * extending this class (be sure to call super()) so that AI and other things can be added. + */ + public EntitySummonedCreature(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world); + this.setPosition(x, y, z); + this.casterReference = new WeakReference(caster); + this.experienceValue = 0; + this.lifetime = lifetime; + } + + // Implementations + + @Override + public void setRevengeTarget(EntityLivingBase entity){ + if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); + } + + @Override + public void onUpdate(){ + super.onUpdate(); + this.updateDelegate(); + } + + @Override + public void onSpawn(){} + + @Override + public void onDespawn(){} + + @Override + public boolean hasParticleEffect() { + return false; + } + + @Override + protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { + // In this case, the delegate method determines whether super is called. + // Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements. + return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + this.writeNBTDelegate(nbttagcompound); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + this.writeNBTDelegate(nbttagcompound); + } + + // Recommended overrides + + @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } + @Override protected boolean canDropLoot(){ return false; } + @Override protected Item getDropItem(){ return null; } + @Override protected ResourceLocation getLootTable(){ return null; } + @Override public boolean canPickUpLoot(){ return false; } + // This vanilla method has nothing to do with the custom onDespawn() method. + @Override protected boolean canDespawn(){ return false; } + + @Override + public boolean canAttackClass(Class entityType){ + // Returns true unless the given entity type is a flying entity and this entity only has melee attacks. + return !EntityFlying.class.isAssignableFrom(entityType) || this.hasRangedAttack(); + } + + // TODO: Backport the following two methods to 1.7.10. + @Override + public ITextComponent getDisplayName(){ + if(getCaster() != null){ + return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(), + new TextComponentTranslation("entity." + this.getEntityString() + ".name")); + }else{ + return super.getDisplayName(); + } + } + + @Override + public boolean hasCustomName(){ + // If this returns true, the renderer will show the nameplate when looking directly at the entity + return Wizardry.settings.showSummonedCreatureNames && getCaster() != null; + } + + // Specific to EntitySummonedCreature, remove if copying + + /** Whether this summoned creature has a ranged attack. Used to test whether it should attack flying creatures. */ + public abstract boolean hasRangedAttack(); +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityWizard.java b/src/main/java/electroblob/wizardry/entity/living/EntityWizard.java new file mode 100644 index 00000000..d2a419c3 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityWizard.java @@ -0,0 +1,803 @@ +package electroblob.wizardry.entity.living; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Random; +import java.util.Set; + +import com.google.common.base.Predicate; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardryAchievements; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WandHelper; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityAgeable; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.IEntityLivingData; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAILookAtTradePlayer; +import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; +import net.minecraft.entity.ai.EntityAIOpenDoor; +import net.minecraft.entity.ai.EntityAIRestrictOpenDoor; +import net.minecraft.entity.ai.EntityAISwimming; +import net.minecraft.entity.ai.EntityAITradePlayer; +import net.minecraft.entity.ai.EntityAIWander; +import net.minecraft.entity.ai.EntityAIWatchClosest; +import net.minecraft.entity.ai.EntityAIWatchClosest2; +import net.minecraft.entity.effect.EntityLightningBolt; +import net.minecraft.entity.monster.IMob; +import net.minecraft.entity.passive.EntityVillager; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.init.MobEffects; +import net.minecraft.init.SoundEvents; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagInt; +import net.minecraft.nbt.NBTTagLong; +import net.minecraft.network.datasync.DataParameter; +import net.minecraft.network.datasync.DataSerializers; +import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.village.MerchantRecipe; +import net.minecraft.village.MerchantRecipeList; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.World; +import net.minecraftforge.common.util.Constants.NBT; +import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; +import net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfession; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.oredict.OreDictionary; + +public class EntityWizard extends EntityVillager implements ISpellCaster, IEntityAdditionalSpawnData { + + /* + * After much debugging, the error in the compiled mod (outside of eclipse) was traced back to this class, + * specifically the methods copied in from EntityVillager when I changed this class to extend it. This figures, + * since I had 1.2.1 working just fine before I did that, and it was the only thing I changed. Apparently, + * methods and fields with obfuscated names like func_129090_a can cause problems when compiled. One of the + * ones here was renamed and the other deleted since it was never called. Watch out for this in future (unless, + * of course, they are overriding something, in which case it should be fine). + */ + + // Extending EntityVillager turned out to be a pretty neat thing to do, since now zombies will attack wizards + + private EntityAIAttackSpell spellCastingAI = new EntityAIAttackSpell(this, 0.5D, 14.0F, 30, 50); + + public int textureIndex = 0; + + /** The entity selector passed into the new AI methods. */ + protected Predicate targetSelector; + + /** Copy of EntityVillager's buyingList, renamed to avoid confusion. */ + private MerchantRecipeList trades; + private int timeUntilReset; + + /** addDefaultEquipmentAndRecipies is called if this is true */ + private boolean updateRecipes; + + /** Data parameter for the cooldown time for wizards healing themselves. */ + private static final DataParameter HEAL_COOLDOWN = EntityDataManager.createKey(EntityWizard.class, DataSerializers.VARINT); + /** Data parameter for the wizard's element. */ + private static final DataParameter ELEMENT = EntityDataManager.createKey(EntityWizard.class, DataSerializers.VARINT); + + // Field implementations + private List spells = new ArrayList(4); + private Spell continuousSpell; + + /** A set of the positions of the blocks that are part of this wizard's tower. */ + private Set towerBlocks; + + public EntityWizard(World world){ + super(world); + this.detachHome(); + // For some reason this can't be in initEntityAI + this.tasks.addTask(3, this.spellCastingAI); + } + + @Override + protected void entityInit(){ + super.entityInit(); + this.dataManager.register(HEAL_COOLDOWN, -1); + this.dataManager.register(ELEMENT, 0); + } + + @Override + protected void initEntityAI(){ + + this.tasks.addTask(0, new EntityAISwimming(this)); + this.tasks.addTask(1, new EntityAITradePlayer(this)); + this.tasks.addTask(1, new EntityAILookAtTradePlayer(this)); + this.tasks.addTask(4, new EntityAIRestrictOpenDoor(this)); + this.tasks.addTask(5, new EntityAIOpenDoor(this, true)); + this.tasks.addTask(6, new EntityAIMoveTowardsRestriction(this, 0.6D)); + this.tasks.addTask(7, new EntityAIWatchClosest2(this, EntityPlayer.class, 3.0F, 1.0F)); + this.tasks.addTask(7, new EntityAIWatchClosest2(this, EntityWizard.class, 5.0F, 0.02F)); + this.tasks.addTask(7, new EntityAIWander(this, 0.6D)); + this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F)); + + this.targetSelector = new Predicate(){ + + public boolean apply(Entity entity){ + + // If the target is valid and not invisible... + if(entity != null && !entity.isInvisible() && WizardryUtilities.isValidTarget(EntityWizard.this, entity)){ + + //... and is a mob, a summoned creature ... + if((entity instanceof IMob || entity instanceof ISummonedCreature + // ... or in the whitelist ... + || Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))) + // ... and isn't in the blacklist ... + && !Arrays.asList(Wizardry.settings.summonedCreatureTargetsBlacklist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){ + // ... it can be attacked. + return true; + } + } + + return false; + } + }; + + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + // By default, wizards don't attack players unless the player has attacked them. + this.targetTasks.addTask(0, new EntityAINearestAttackableTarget(this, EntityLiving.class, 0, false, true, this.targetSelector)); + } + + @Override + protected void applyEntityAttributes(){ + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.5); + } + + private int getHealCooldown(){ + return this.dataManager.get(HEAL_COOLDOWN); + } + + private void setHealCooldown(int cooldown){ + this.dataManager.set(HEAL_COOLDOWN, cooldown); + } + + public Element getElement(){ + return Element.values()[this.dataManager.get(ELEMENT)]; + } + + public void setElement(Element element){ + this.dataManager.set(ELEMENT, element.ordinal()); + } + + @Override + public List getSpells(){ + return this.spells; + } + + @Override + public SpellModifiers getModifiers(){ + return new SpellModifiers(); + } + + @Override + public void setContinuousSpell(Spell spell){ + this.continuousSpell = spell; + } + + @Override + public Spell getContinuousSpell(){ + return this.continuousSpell; + } + + @Override + public void onLivingUpdate(){ + + super.onLivingUpdate(); + + // Still better to store this to a local variable as it's almost certainly more efficient. + int healCooldown = this.getHealCooldown(); + + // This is now done slightly differently because isPotionActive doesn't work on client here, meaning that when + // affected with arcane jammer and healCooldown == 0, whilst the wizard didn't actually heal or play the sound, + // the particles still spawned, and since healCooldown wasn't reset they spawned every tick until the arcane + // jammer wore off. + if(healCooldown == 0 && this.getHealth() < this.getMaxHealth() && this.getHealth() > 0 && !this.isPotionActive(WizardryPotions.arcane_jammer)){ + + // Healer wizards use greater heal. + this.heal(this.getElement() == Element.HEALING ? 8 : 4); + this.setHealCooldown(-1); + + // deathTime == 0 checks the wizard isn't currently dying + }else if(healCooldown == -1 && this.deathTime == 0){ + + // Heal particles + if(worldObj.isRemote){ + for(int i=0; i<10; i++){ + double d0 = (double)((float)this.posX + rand.nextFloat()*2 - 1.0F); + // Apparently the client side spawns the particles 1 block higher than it should... hence the - 0.5F. + double d1 = (double)((float)this.posY - 0.5F + rand.nextFloat()); + double d2 = (double)((float)this.posZ + rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, d0, d1, d2, 0, 0.1F, 0, 48 + rand.nextInt(12), 1.0f, 1.0f, 0.3f); + } + }else{ + if(this.getHealth() < 10){ + // Wizard heals himself more often if he has low health + this.setHealCooldown(150); + }else{ + this.setHealCooldown(400); + } + + this.playSound(WizardrySounds.SPELL_HEAL, 0.7F, rand.nextFloat() * 0.4F + 1.0F); + } + } + + if(healCooldown > 0){ + this.setHealCooldown(healCooldown-1); + } + } + + @Override + protected void updateAITasks(){ + + if(!this.isTrading() && this.timeUntilReset > 0){ + + --this.timeUntilReset; + + if(this.timeUntilReset <= 0){ + + if(this.updateRecipes){ + + for(MerchantRecipe merchantrecipe : this.trades){ + + if(merchantrecipe.isRecipeDisabled()){ + // Increases the number of allowed uses of a disabled recipe by a random number. + merchantrecipe.increaseMaxTradeUses(this.rand.nextInt(6) + this.rand.nextInt(6) + 2); + } + } + + if(this.trades.size() < 12){ + this.addRandomRecipes(1); + } + + this.updateRecipes = false; + } + + this.addPotionEffect(new PotionEffect(MobEffects.REGENERATION, 200, 0)); + } + } + + // Super call removed because EntityVillager's version does things I don't want and the next one up is + // in EntityLivingBase and does nothing. + } + + @Override + public boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { + + // Debugging + //player.addChatComponentMessage(new TextComponentTranslation("wizard.debug", Spell.get(spells[1]).getDisplayName(), Spell.get(spells[2]).getDisplayName(), Spell.get(spells[3]).getDisplayName())); + + // When right-clicked with a spell book in creative, sets one of the spells to that spell + if(player.capabilities.isCreativeMode && stack != null && stack.getItem() instanceof ItemSpellBook){ + if(this.spells.size() >= 4 && Spell.get(stack.getItemDamage()).canBeCastByNPCs()){ + this.spells.set(rand.nextInt(3)+1, Spell.get(stack.getItemDamage())); + return true; + } + } + + // Won't trade with a player that has attacked them. + if (this.isEntityAlive() && !this.isTrading() && !this.isChild() && !player.isSneaking() && this.getAttackTarget() != player) + { + if (!this.worldObj.isRemote) + { + this.setCustomer(player); + player.displayVillagerTradeGui(this); + //player.displayGUIMerchant(this, this.getElement().getWizardName()); + } + + return true; + } + else + { + return false; + } + } + + @Override + public ITextComponent getDisplayName() { + return this.getElement().getWizardName(); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbt){ + + super.writeEntityToNBT(nbt); + + if (this.trades != null){ + nbt.setTag("trades", this.trades.getRecipiesAsTags()); + } + + nbt.setInteger("element", this.getElement().ordinal()); + nbt.setInteger("skin", this.textureIndex); + nbt.setTag("spells", WizardryUtilities.listToNBT(spells, spell -> new NBTTagInt(spell.id()))); + + if(this.towerBlocks != null && this.towerBlocks.size() > 0){ + nbt.setTag("towerBlocks", WizardryUtilities.listToNBT(this.towerBlocks, pos -> new NBTTagLong(pos.toLong()))); + } + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbt){ + + super.readEntityFromNBT(nbt); + + if(nbt.hasKey("trades")){ + NBTTagCompound nbttagcompound1 = nbt.getCompoundTag("trades"); + this.trades = new MerchantRecipeList(nbttagcompound1); + } + + this.setElement(Element.values()[nbt.getInteger("element")]); + this.textureIndex = nbt.getInteger("skin"); + this.spells = (List) WizardryUtilities.NBTToList(nbt.getTagList("spells", NBT.TAG_INT), + (NBTTagInt tag) -> Spell.get(tag.getInt())); + + this.towerBlocks = new HashSet(WizardryUtilities.NBTToList(nbt.getTagList("towerBlocks", + NBT.TAG_LONG), (NBTTagLong tag) -> BlockPos.fromLong(tag.getLong()))); + } + + @Override + protected boolean canDespawn(){ + return false; + } + + @Override + public boolean isTrading() + { + return this.getCustomer() != null; + } + + @Override + public void useRecipe(MerchantRecipe merchantrecipe){ + + merchantrecipe.incrementToolUses(); + this.livingSoundTime = -this.getTalkInterval(); + this.playSound(SoundEvents.ENTITY_VILLAGER_YES, this.getSoundVolume(), this.getSoundPitch()); + + // Achievements + if (this.getCustomer() != null) + { + this.getCustomer().addStat(WizardryAchievements.wizard_trade); + + if(merchantrecipe.getItemToSell().getItem() instanceof ItemSpellBook + && Spell.get(merchantrecipe.getItemToSell().getItemDamage()).tier == Tier.MASTER){ + this.getCustomer().addStat(WizardryAchievements.buy_master_spell); + } + } + + // Changed to a 4 in 5 chance of unlocking a new recipe. + if(this.rand.nextInt(5) > 0){ + this.timeUntilReset = 40; + this.updateRecipes = true; + + if (this.getCustomer() != null) + { + this.getCustomer().getName(); + } + else + { + } + } + } + + // This is called from the gui in order to display the recipes (no surprise there), and this is actually where + // the initialisation is done, i.e. the trades don't actually exist until some player goes to trade with the + // villager, at which point the first is added. + @Override + public MerchantRecipeList getRecipes(EntityPlayer par1EntityPlayer){ + + if(this.trades == null){ + + this.trades = new MerchantRecipeList(); + + // All wizards will buy spell books + ItemStack anySpellBook = new ItemStack(WizardryItems.spell_book, 1, OreDictionary.WILDCARD_VALUE); + ItemStack crystalStack = new ItemStack(WizardryItems.magic_crystal, 5); + + // NOTE: For wizardry 1.2, increase the number of uses of this trade. The default is 7, for reference. + this.trades.add(new MerchantRecipe(anySpellBook, crystalStack)); + + this.addRandomRecipes(3); + } + + return this.trades; + } + + /** + * This is called once on initialisation and then once each time the wizard gains new trades (the particle thingy). + */ + private void addRandomRecipes(int numberOfItemsToAdd){ + + MerchantRecipeList merchantrecipelist; + merchantrecipelist = new MerchantRecipeList(); + + for(int i=0; i spells = Spell.getSpells(new Spell.TierElementFilter(tier, null)); + // All enabled spells of the given tier that match this wizard's element + List specialismSpells = Spell.getSpells(new Spell.TierElementFilter(tier, this.getElement())); + + // This code is sooooooo much neater with the new filter system! + switch(tier){ + + case BASIC: + randomiser = rand.nextInt(5); + if(randomiser < 4 && !spells.isEmpty()){ + if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){ + // This means it is more likely for spell books sold to be of the same element as the wizard if the wizard has an element. + return new ItemStack(WizardryItems.spell_book, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id()); + }else{ + return new ItemStack(WizardryItems.spell_book, 1, spells.get(rand.nextInt(spells.size())).id()); + } + }else{ + if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){ + // This means it is more likely for wands sold to be of the same element as the wizard if the wizard has an element. + return new ItemStack(WizardryUtilities.getWand(tier, this.getElement())); + }else{ + return new ItemStack(WizardryUtilities.getWand(tier, Element.values()[rand.nextInt(Element.values().length)])); + } + } + + case APPRENTICE: + randomiser = rand.nextInt(Wizardry.settings.discoveryMode ? 12 : 10); + if(randomiser < 5 && !spells.isEmpty()){ + if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){ + // This means it is more likely for spell books sold to be of the same element as the wizard if the wizard has an element. + return new ItemStack(WizardryItems.spell_book, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id()); + }else{ + return new ItemStack(WizardryItems.spell_book, 1, spells.get(rand.nextInt(spells.size())).id()); + } + }else if(randomiser < 6){ + if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){ + // This means it is more likely for wands sold to be of the same element as the wizard if the wizard has an element. + return new ItemStack(WizardryUtilities.getWand(tier, this.getElement())); + }else{ + return new ItemStack(WizardryUtilities.getWand(tier, Element.values()[rand.nextInt(Element.values().length)])); + } + }else if(randomiser < 8){ + return new ItemStack(WizardryItems.arcane_tome, 1, 1); + }else if(randomiser < 10){ + EntityEquipmentSlot slot = WizardryUtilities.ARMOUR_SLOTS[rand.nextInt(WizardryUtilities.ARMOUR_SLOTS.length)]; + if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){ + // This means it is more likely for armour sold to be of the same element as the wizard if the wizard has an element. + return new ItemStack(WizardryUtilities.getArmour(this.getElement(), slot)); + }else{ + return new ItemStack(WizardryUtilities.getArmour(Element.values()[rand.nextInt(Element.values().length)], slot)); + } + }else{ + // Don't need to check for discovery mode here since it is done above + return new ItemStack(WizardryItems.identification_scroll); + } + + case ADVANCED: + randomiser = rand.nextInt(12); + if(randomiser < 5 && !spells.isEmpty()){ + if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){ + // This means it is more likely for spell books sold to be of the same element as the wizard if the wizard has an element. + return new ItemStack(WizardryItems.spell_book, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id()); + }else{ + return new ItemStack(WizardryItems.spell_book, 1, spells.get(rand.nextInt(spells.size())).id()); + } + }else if(randomiser < 6){ + if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){ + // This means it is more likely for wands sold to be of the same element as the wizard if the wizard has an element. + return new ItemStack(WizardryUtilities.getWand(tier, this.getElement())); + }else{ + return new ItemStack(WizardryUtilities.getWand(tier, Element.values()[rand.nextInt(Element.values().length)])); + } + }else if(randomiser < 8){ + return new ItemStack(WizardryItems.arcane_tome, 1, 2); + }else{ + List upgrades = new ArrayList(WandHelper.getSpecialUpgrades()); + randomiser = rand.nextInt(upgrades.size()); + return new ItemStack(upgrades.get(randomiser)); + } + + case MASTER: + // If a regular wizard rolls a master trade, it can only be a simple master wand or a tome of arcana + randomiser = this.getElement() != Element.MAGIC ? rand.nextInt(8) : 5 + rand.nextInt(3); + + if(randomiser < 5 && this.getElement() != Element.MAGIC && !specialismSpells.isEmpty()){ + // Master spells can only be sold by a specialist in that element. + return new ItemStack(WizardryItems.spell_book, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id()); + + }else if(randomiser < 6){ + if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){ + // Master elemental wands can only be sold by a specialist in that element. + return new ItemStack(WizardryUtilities.getWand(tier, this.getElement())); + }else{ + return new ItemStack(WizardryItems.master_wand); + } + }else{ + return new ItemStack(WizardryItems.arcane_tome, 1, 3); + } + } + + return new ItemStack(Blocks.STONE); + } + + @Override + public void setProfession(VillagerProfession prof) { + // Disables Forge's stuff. + } + + @Override + public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData livingdata){ + + livingdata = super.onInitialSpawn(difficulty, livingdata); + + textureIndex = this.rand.nextInt(6); + + if(rand.nextBoolean()){ + this.setElement(Element.values()[rand.nextInt(Element.values().length - 1) + 1]); + }else{ + this.setElement(Element.MAGIC); + } + + Element element = this.getElement(); + + // Adds armour. + for(EntityEquipmentSlot slot : WizardryUtilities.ARMOUR_SLOTS){ + this.setItemStackToSlot(slot, new ItemStack(WizardryUtilities.getArmour(element, slot))); + } + + // Default chance is 0.085f, for reference. + for(EntityEquipmentSlot slot : EntityEquipmentSlot.values()) this.setDropChance(slot, 0.0f); + + // All wizards know magic missile, even if it is disabled. + spells.add(Spells.magic_missile); + + Tier maxTier = populateSpells(spells, element, 3, rand); + + // Now done after the spells so it can take the tier into account. + this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(WizardryUtilities.getWand(maxTier, element))); + + return livingdata; + } + + /** + * Adds n random spells to the given list. The spells will be of the given element if possible. Extracted as a + * separate function since it was the same in both EntityWizard and EntityEvilWizard. + * @param spells The spell list to be populated. + * @param e The element that the spells should belong to, or {@link Element#MAGIC} for a random element each time. + * @param n The number of spells to add. + * @param random A random number generator to use. + * @return The tier of the highest-tier spell that was added to the list. + */ + static Tier populateSpells(List spells, Element e, int n, Random random){ + + // This is the tier of the highest tier spell added. + Tier maxTier = Tier.BASIC; + + List npcSpells = Spell.getSpells(Spell.npcSpells); + + for(int i=0; i<3; i++){ + + Tier tier; + // If the wizard has no element, it picks a random one each time. + Element element = e == Element.MAGIC ? Element.values()[random.nextInt(Element.values().length)] : e; + + int randomiser = random.nextInt(20); + + // Uses its own special weighting + if(randomiser < 10){ + tier = Tier.BASIC; + }else if(randomiser < 16){ + tier = Tier.APPRENTICE; + }else{ + tier = Tier.ADVANCED; + } + + if(tier.ordinal() > maxTier.ordinal()) maxTier = tier; + + // Finds all the spells of the chosen tier and element + List list = Spell.getSpells(new Spell.TierElementFilter(tier, element)); + // Keeps only spells which can be cast by NPCs + list.retainAll(npcSpells); + // Removes spells that the wizard already has + list.removeAll(spells); + + // Ensures the tier chosen actually has spells in it. (isEmpty() is exactly the same as size() == 0) + if(list.isEmpty()){ + // If there are no spells applicable, tier and element restrictions are removed to give maximum + // possibility of there being an applicable spell. + list = npcSpells; + // Removes spells that the wizard already has + list.removeAll(spells); + } + + // If the list is still empty now, there must be less than 3 enabled spells that can be cast by wizards + // (excluding magic missile). In this case, having empty slots seems reasonable. + if(!list.isEmpty()) spells.add(list.get(random.nextInt(list.size()))); + + } + + return maxTier; + } + + @Override + public void writeSpawnData(ByteBuf data){ + data.writeInt(textureIndex); + } + + @Override + public void readSpawnData(ByteBuf data){ + textureIndex = data.readInt(); + } + + @Override + public boolean attackEntityFrom(DamageSource source, float damage){ + + if(source.getEntity() instanceof EntityPlayer){ + ((EntityPlayer)source.getEntity()).addStat(WizardryAchievements.anger_wizard); + } + + return super.attackEntityFrom(source, damage); + } + + /** + * Sets the list of blocks that are part of this wizard's tower. If a player breaks any of these blocks, the wizard + * will get angry and attack them. + * @param blocks A Set of BlockPos objects representing the blocks in the tower. + */ + public void setTowerBlocks(Set blocks){ + this.towerBlocks = blocks; + } + + /** + * Tests whether the block at the given coordinates is part of this wizard's tower. + * @param x + * @param y + * @param z + * @return + */ + public boolean isBlockPartOfTower(BlockPos pos){ + if(this.towerBlocks == null) return false; + // Uses .equals() rather than == so this will work fine. + return this.towerBlocks.contains(pos); + } + + // EntityVillager overrides (that don't add features) + + @Override public boolean isMating(){ return false; } + @Override public void setMating(boolean p_70947_1_){} + @Override public void setPlaying(boolean p_70939_1_){} + @Override public boolean isPlaying(){ return false; } + @Override public void setLookingForHome(){} + // Doesn't say it, but this is in fact nullable. + @Override public EntityVillager createChild(EntityAgeable par1EntityAgeable){ return null; } + @SideOnly(Side.CLIENT) + @Override public void setRecipes(MerchantRecipeList par1MerchantRecipeList){} + @Override + public void onStruckByLightning(EntityLightningBolt lightningBolt){ + // Restores the normal behaviour, replacing EntityVillager's witch conversion. + this.attackEntityFrom(DamageSource.lightningBolt, 5.0F); + // Entity's version does something strange with the private fire variable, but since I don't have access this + // will probably be fine. + this.setFire(8); + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityZombieMinion.java b/src/main/java/electroblob/wizardry/entity/living/EntityZombieMinion.java new file mode 100644 index 00000000..c1ac3a7a --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntityZombieMinion.java @@ -0,0 +1,175 @@ +package electroblob.wizardry.entity.living; + +import java.lang.ref.WeakReference; +import java.util.UUID; + +import electroblob.wizardry.Wizardry; +import net.minecraft.entity.EntityFlying; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAIMoveThroughVillage; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; +import net.minecraft.entity.monster.EntityZombie; +import net.minecraft.entity.monster.ZombieType; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.World; + +public class EntityZombieMinion extends EntityZombie implements ISummonedCreature { + + // Field implementations + private int lifetime = 600; + private WeakReference casterReference; + private UUID casterUUID; + + // Setter + getter implementations + @Override public int getLifetime(){ return lifetime; } + @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } + @Override public WeakReference getCasterReference(){ return casterReference; } + @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } + @Override public UUID getCasterUUID() { return casterUUID; } + @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } + + /** + * Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't + * matter because the client side entity immediately gets the lifetime value copied over to it by this class + * anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's + * no need to do anything inside it other than call super(). + */ + public EntityZombieMinion(World world){ + super(world); + this.experienceValue = 0; + } + + /** + * Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when + * extending this class (be sure to call super()) so that AI and other things can be added. + */ + public EntityZombieMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world); + this.setPosition(x, y, z); + this.casterReference = new WeakReference(caster); + this.experienceValue = 0; + this.lifetime = lifetime; + } + + // EntityZombie overrides (EntityZombie is a long class so there are lots of these) + + @Override + protected void applyEntityAI() + { + this.tasks.addTask(6, new EntityAIMoveThroughVillage(this, 1.0D, false)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, + 0, false, true, this.getTargetSelector())); + } + + @Override public boolean isChild(){ return false; } + @Override public void setChild(boolean childZombie){} + @Override public ZombieType getZombieType(){ return ZombieType.NORMAL; } + @Override public boolean isVillager(){ return false; } + @Override public net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfession getVillagerTypeForge(){ return null; } + @Override protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty){} // They don't have equipment! + @Override public void onKillEntity(EntityLivingBase entityLivingIn){} // Turns villagers to zombies in EntityZombie + @Override protected void startConversion(int ticks){} + @Override public boolean isConverting(){ return false; } + @Override protected void convertToVillager(){} + @Override protected int getConversionTimeBoost(){ return 0; } + @Override public void setChildSize(boolean isChild){} + + // Implementations + + @Override + public void setRevengeTarget(EntityLivingBase entity){ + if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); + } + + @Override + public void onUpdate(){ + super.onUpdate(); + this.updateDelegate(); + } + + @Override + public void onSpawn(){ + this.spawnParticleEffect(); + } + + @Override + public void onDespawn(){ + this.spawnParticleEffect(); + } + + private void spawnParticleEffect(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + this.worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); + } + } + } + + @Override + public boolean hasParticleEffect() { + return true; + } + + @Override + public boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { + // In this case, the delegate method determines whether super is called. + // Rather handily, we can make use of Java's 'stop as soon as you find true' method of evaluating OR statements. + return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + this.writeNBTDelegate(nbttagcompound); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + this.writeNBTDelegate(nbttagcompound); + } + + // Recommended overrides + + @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } + @Override protected boolean canDropLoot(){ return false; } + @Override protected Item getDropItem(){ return null; } + @Override protected ResourceLocation getLootTable(){ return null; } + @Override public boolean canPickUpLoot(){ return false; } + // This vanilla method has nothing to do with the custom despawn() method. + @Override protected boolean canDespawn(){ return false; } + + @Override + public boolean canAttackClass(Class entityType){ + // Returns true unless the given entity type is a flying entity. + return !EntityFlying.class.isAssignableFrom(entityType); + } + + @Override + public ITextComponent getDisplayName(){ + if(getCaster() != null){ + return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(), + new TextComponentTranslation("entity." + this.getEntityString() + ".name")); + }else{ + return super.getDisplayName(); + } + } + + @Override + public boolean hasCustomName(){ + // If this returns true, the renderer will show the nameplate when looking directly at the entity + return Wizardry.settings.showSummonedCreatureNames && getCaster() != null; + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/entity/living/IIntelligentSpellCaster.java b/src/main/java/electroblob/wizardry/entity/living/IIntelligentSpellCaster.java new file mode 100644 index 00000000..1bcb7c86 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/IIntelligentSpellCaster.java @@ -0,0 +1,20 @@ +package electroblob.wizardry.entity.living; + +import java.util.List; + +import electroblob.wizardry.spell.Spell; + +/** [NYI] Interface for entities that can select between spells based on their current circumstances, to be used in + * conjunction with {@link EntityAISelectSpell}. */ +public interface IIntelligentSpellCaster extends ISpellCaster { + + /** Called from {@link EntityAISelectSpell} to set the spells that the entity can use in its current situation. + * Implementors should assign the given list to some internal field and retrieve it when {@link #getSpells()} is + * called. */ + public void setCurrentSpells(List spells); + + /** Returns a list of spells that this entity 'knows'. The entity's current spell will be selected from this list + * based on the current situation: whether it is attacking, its health, etc. Most likely, you will want to return a + * constant list, but it may change for some reason - perhaps if the entity 'learns' a new spell. */ + public List getKnownSpells(); +} diff --git a/src/main/java/electroblob/wizardry/entity/living/ISpellCaster.java b/src/main/java/electroblob/wizardry/entity/living/ISpellCaster.java new file mode 100644 index 00000000..ce2733df --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/ISpellCaster.java @@ -0,0 +1,61 @@ +package electroblob.wizardry.entity.living; + +import java.util.List; + +import javax.annotation.Nonnull; + +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.SpellModifiers; + +/** Interface for entities that can cast spells. Mainly intended for use by wizard-type entities, but can be + * implemented by any subclass of EntityLiving. Designed to be as flexible as possible - ranging from the simplest + * use of giving an entity a specific spell as an attack, to a complex AI which selects different spell types + * depending on the situation. The only restriction is that the spells must be castable by NPCs. + *

    + * This is intended for entities that use {@link EntityAIAttackSpell}. If so, all the spell casting code (including + * packets) is handled by that class, and all the implementor needs to do is decide which spell(s) to select. + *

    + * This class also allows Wizardry to do all the syncing necessary for continuous spell casting. All the implementor + * needs to do is store the actual fields involved. + */ +/* Perhaps this should be a capability? Though I can't help thinking they're mainly for attaching data to vanilla + * classes, rather than custom ones. For now, the main purpose of this is to centralise code within wizardry itself, + * and I may as well make it an API feature - but I'm not writing a capability unless someone sees a reason to attach + * it to a class they don't own, and I don't see that happening anytime soon. */ +// For even more fun, combine this with an ISummonedCreature and have skeletons that can cast spells! +public interface ISpellCaster { + + /** + * Called each time the entity attacks to get the spells that can be cast. For simple implementations, just + * return a list of size one containing the spell that the entity uses as an attack, perhaps performing some + * simple logic within this method. For more intelligent implementations based on spell types, consider using + * {@link IIntelligentSpellCaster} instead. + * @return A list of {@link Spell} instances. A random spell from this list will be cast when the entity attacks. + * The list will not be modified by the AI class and can therefore be an immutable list. The spells in the list + * must be castable by NPCs (i.e. {@link Spell#canBeCastByNPCs()} returns true). + */ + @Nonnull + public List getSpells(); + + /** + * Called each time the entity attacks to get the modifiers to apply to the spell. + * @return A {@link SpellModifiers} object representing the modifiers to apply to the spell. If no modifiers are required, + * pass in an empty {@code SpellModifiers} object. + */ + @Nonnull + public SpellModifiers getModifiers(); + + /** Returns the continuous spell that is currently being cast, or the None spell if there is none. Implementors should simply + * store this as a private field and return it here. Will be synced by the AI class, but whether it is saved to NBT + * is up to you. If the implementing class does not deal with continuous spells, just return {@link Spells#none}. If the + * implementing class only ever uses one continuous spell, do not just return that spell; the field must still + * be stored. */ + @Nonnull + public Spell getContinuousSpell(); + + /** Sets the continuous spell that is currently being cast, or the None spell if there is none. Implementors should simply + * store this as a private field and assign it here. Will be synced by the AI class, but whether it is saved to NBT + * is up to you. If the implementing class does not deal with continuous spells, leave this method blank. */ + public void setContinuousSpell(Spell spell); +} diff --git a/src/main/java/electroblob/wizardry/entity/living/ISummonedCreature.java b/src/main/java/electroblob/wizardry/entity/living/ISummonedCreature.java new file mode 100644 index 00000000..cc5a3a35 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/ISummonedCreature.java @@ -0,0 +1,278 @@ +package electroblob.wizardry.entity.living; + +import java.lang.ref.WeakReference; +import java.util.Arrays; +import java.util.Locale; +import java.util.UUID; + +import javax.annotation.Nullable; + +import com.google.common.base.Predicate; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryEventHandler; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.IMob; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumHand; +import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; + +/** Interface for all summoned creatures. The code for summoned creatures has been overhauled in Wizardry 1.2, and this + * interface allows summoned creatures to extend vanilla (or indeed modded) entity classes, so EntitySummonedZombie + * now extends EntityZombie, for example. This change has two major benefits: + *

    + * - There is no longer any need for separate render classes, because summoned creatures are now instances of vanilla + * types. You don't even need to assign a render class because the supertype should already be assigned the correct one.
    + * - Summoned creature classes are now much more robust when it comes to changes between Minecraft versions, since none + * of the vanilla code needs to be copied. + *

    + * Summoned creatures that do not emulate vanilla entities do not directly implement this interface. Instead, they + * should extend the abstract base implementation, {@link EntitySummonedCreature}. + *

    + * All damage dealt by ISummonedCreature instances is redirected via {@link WizardryEventHandler#onLivingAttackEvent( + * net.minecraftforge.event.entity.living.LivingAttackEvent) WizardryEventHandler.onLivingAttackEvent(LivingAttackEvent)} + * and replaced by an instance of {@link electroblob.wizardry.util.IElementalDamage IElementalDamage} with the summoner + * of that creature as the source rather than the creature itself. This means that kills by summoned creatures register + * as player kills, dropping xp and rare loot. + *

    + * Though this system is a lot better than the previous system, it is not a perfect solution. The old + * EntitySummonedCreature class overrode some methods from Entity in order to add shared functionality, but this cannot + * be done with an interface. To get around this problem, this interface contains 5 delegate methods that do the same + * things, with the aim of centralising as much code as possible, even though it is not automatically applied. + * Implementing classes must override the corresponding methods from Entity, and within them, call the appropriate + * delegate method in this interface. It is impossible to enforce this condition, but the summoned creature will not + * work properly unless it is adhered to. The position of the delegate method call is unimportant, but by convention + * it is usually at the start of the calling method, which avoids it being unintentionally skipped by a return statement. + *

    + * It is recommended that when implementing this interface, you begin by copying {@link EntitySummonedCreature} to ensure + * all the relevant methods are duplicated. You can then change the superclass, override any additional methods and add + * functionality to any that are already overridden. You will always want to override the AI methods at the very least. + *

    + * Due to the limitations of interfaces, some methods that really ought to be protected are public. These are clearly + * marked as 'Internal, DO NOT CALL'. Don't call them, only implement them. + * @since Wizardry 1.2 + * @author Electroblob */ +/* + * Quite honestly, this is not what default methods are really for. However, this is modding, and in modding some + * sacrifices have to be made when it comes to Java style - because adding on to a pre-existing program is not a + * good way of doing this sort of thing anyway, but we have no choice about that! + */ +public interface ISummonedCreature extends IEntityAdditionalSpawnData { + + // Remember that ALL fields are static and final in interfaces, even if they don't explicitly state that. + String NAMEPLATE_TRANSLATION_KEY = "entity.wizardry.summonedcreature.nameplate"; + + // Setters and getters. The subclass fields that these access should be private. + + /** Sets the lifetime of the summoned creature in ticks. */ + void setLifetime(int ticks); + + /** Returns the lifetime of the summoned creature in ticks. Allows primarily for duration multiplier support, but + * also for example the skeleton legion spell which lasts for 60 seconds instead of the usual 30. Syncing and saving + * is done automatically. As of Wizardry 1.2, despawning is handled in ISummonedCreature; see + * {@link ISummonedCreature#onDespawn()} for details. */ + int getLifetime(); + + /** Sets the WeakReference object which refers to the owner of this summoned creature. Internal, don't call + * unless you know what you are doing. */ + void setCasterReference(WeakReference reference); + + /** Returns a WeakReference object which refers to the owner of this summoned creature. Subclasses should store this + * as a private field. This may be null; as such it is preferable to use {@link ISummonedCreature#getCaster()} to get + * the caster object itself. */ + @Nullable + WeakReference getCasterReference(); + + /** Internal, DO NOT CALL. */ + void setCasterUUID(UUID uuid); + /** Internal, DO NOT CALL. This is for loading purposes only and is not usually synchronised. */ + UUID getCasterUUID(); + + /** Returns the EntityLivingBase that summoned this creature, or null if it no longer exists. Cases where the + * entity may no longer exist are: entity died or was deleted, mob despawned, player logged out, entity teleported + * to another dimension, or this creature simply had no caster in the first place. This is the correct method + * to use to get the owner of this summoned creature. */ + @Nullable + default EntityLivingBase getCaster(){ + return getCasterReference() == null ? null : getCasterReference().get(); + } + + // Miscellaneous + + /** + * Called by the server when constructing the spawn packet. + * Data should be added to the provided stream. + * Implementors must call super when overriding. + * + * @param buffer The packet data stream + */ + @Override + default void writeSpawnData(ByteBuf buffer) { + buffer.writeInt(getCaster() != null ? getCaster().getEntityId() : -1); + buffer.writeInt(getLifetime()); + } + + /** + * Called by the client when it receives a Entity spawn packet. + * Data should be read out of the stream in the same way as it was written. + * Implementors must call super when overriding. + * + * @param additionalData The packet data stream + */ + @Override + default void readSpawnData(ByteBuf buffer) { + int id = buffer.readInt(); + // We're on the client side here, so we can safely use Minecraft.getMinecraft().theWorld via proxies. + if(id > -1) setCasterReference(new WeakReference((EntityLivingBase)Wizardry.proxy.getTheWorld().getEntityByID(id))); + setLifetime(buffer.readInt()); + } + + /** + * Shorthand for {@link WizardryUtilities#isValidTarget(Entity, Entity)}, with the owner of this creature as the + * attacker. Also allows implementors to override it if they wish to do so. + */ + default boolean isValidTarget(Entity target){ + return WizardryUtilities.isValidTarget(this.getCaster(), target); + } + + /** Returns a entity selector to be passed into AI methods. Normally, this should not be overridden, but it is + * possible for implementors to override this in order to do something special when selecting a target. */ + default Predicate getTargetSelector(){ + + return new Predicate(){ + + public boolean apply(Entity entity){ + // TODO: Backport invisibility check (also in wizards) + // If the target is valid and not invisible... + if(!entity.isInvisible() && isValidTarget(entity)){ + + //... and is a player, they can be attacked, since players can't be in the whitelist or the blacklist. + if(entity instanceof EntityPlayer) return true; + + //... and is a mob, a summoned creature, a wizard ... + if((entity instanceof IMob || entity instanceof ISummonedCreature || (entity instanceof EntityWizard && !(getCaster() instanceof EntityWizard)) + // ... or in the whitelist ... + || Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))) + // ... and isn't in the blacklist ... + && !Arrays.asList(Wizardry.settings.summonedCreatureTargetsBlacklist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){ + // ... it can be attacked. + return true; + } + } + + return false; + } + }; + } + + /** + * Called when this creature has existed for 1 tick, effectively when it has just been spawned. Normally used + * to add particles, sounds, etc. + */ + void onSpawn(); + + /** + * Called when this summoned creature vanishes. Normally used to add particles, sounds, etc. + */ + void onDespawn(); + + /** Whether this creature should spawn a subtle black swirl particle effect while alive. */ + boolean hasParticleEffect(); + + /** Called from the event handler after the damage change is applied. Does nothing by default, but can be overridden + * to do something when a successful attack is made. This was added because the event-based damage source system + * can cause parts of attackEntityAsMob not to fire, since attackEntityFrom is intercepted and canceled. + *

    + * Usage examples: {@link EntitySliverfishMinion} uses this to summon more silverfish if the target is killed, + * {@link EntitySkeletonMinion} and {@link EntitySpiderMinion} use this to add potion effects to the target. */ + default void onSuccessfulAttack(EntityLivingBase target){}; + + // Delegates + + /** Implementors should call this from writeEntityToNBT. Can be overridden as long as super is called, but there's + * very little point in doing that since anything extra could just be added to writeEntityToNBT anyway. */ + default void writeNBTDelegate(NBTTagCompound tagcompound){ + if(this.getCaster() != null){ + tagcompound.setUniqueId("casterUUID", this.getCaster().getUniqueID()); + } + tagcompound.setInteger("lifetime", getLifetime()); + } + + /** Implementors should call this from readEntityFromNBT. Can be overridden as long as super is called, but there's + * very little point in doing that since anything extra could just be added to readEntityFromNBT anyway. */ + default void readNBTDelegate(NBTTagCompound tagcompound){ + this.setCasterUUID(tagcompound.getUniqueId("casterUUID")); + this.setLifetime(tagcompound.getInteger("lifetime")); + } + + /** Implementors should call this from setRevengeTarget, and call super.setRevengeTarget if and only if this method + * returns true. */ + default boolean shouldRevengeTarget(EntityLivingBase entity){ + // Allows the config to prevent minions from revenge-targeting their owners. + return entity != this.getCaster() || Wizardry.settings.minionRevengeTargeting; + } + + /** Implementors should call this from onUpdate. Can be overridden as long as super is called, but there's + * very little point in doing that since anything extra could just be added to onUpdate anyway. */ + default void updateDelegate(){ + + if(!(this instanceof Entity)) throw new ClassCastException("Implementations of ISummonedCreature must extend Entity!"); + + Entity thisEntity = ((Entity)this); + + if(this.getCaster() == null && this.getCasterUUID() != null){ + Entity entity = WizardryUtilities.getEntityByUUID(thisEntity.worldObj, getCasterUUID()); + if(entity instanceof EntityLivingBase){ + this.setCasterReference(new WeakReference((EntityLivingBase)entity)); + } + } + + if(thisEntity.ticksExisted == 1){ + this.onSpawn(); + } + + if(thisEntity.ticksExisted > this.getLifetime() && this.getLifetime() != -1){ + this.onDespawn(); + thisEntity.setDead(); + } + + if(this.hasParticleEffect() && thisEntity.worldObj.isRemote && thisEntity.worldObj.rand.nextInt(8) == 0) + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, thisEntity.worldObj, thisEntity.posX, thisEntity.posY + thisEntity.worldObj.rand.nextDouble()*1.5, thisEntity.posZ, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + + } + + /** Implementors should call this from processInteract, and call super.processInteract if and only if this method + * returns false. */ + default boolean interactDelegate(EntityPlayer player, EnumHand hand, ItemStack stack) { + + WizardData properties = WizardData.get(player); + // Selects one of the player's minions. + if(player.isSneaking() && stack != null && stack.getItem() instanceof ItemWand){ + + if(!player.worldObj.isRemote && properties != null && this.getCaster() == player){ + + if(properties.selectedMinion != null && properties.selectedMinion.get() == this){ + // Deselects the selected minion if right-clicked again + properties.selectedMinion = null; + }else{ + // Selects this minion + properties.selectedMinion = new WeakReference(this); + } + properties.sync(); + } + return true; + } + + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityBomb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityBomb.java new file mode 100644 index 00000000..f383ca21 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityBomb.java @@ -0,0 +1,59 @@ +package electroblob.wizardry.entity.projectile; + +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; + +/** + * Same as {@link EntityMagicProjectile}, but with an additional blast multiplier field which is synced and saved to + * allow for the spread of particles to be changed depending on the blast area. + * @author Electroblob + * @since Wizardry 1.2 + */ +public abstract class EntityBomb extends EntityMagicProjectile implements IEntityAdditionalSpawnData { + + /** The entity blast multiplier. This is now synced and saved centrally from {@link EntityBomb}. */ + public float blastMultiplier = 1.0f; + + public EntityBomb(World world) { + super(world); + } + + public EntityBomb(World world, EntityLivingBase thrower) { + super(world, thrower); + } + + public EntityBomb(World world, EntityLivingBase thrower, float damageMultiplier, float blastMultiplier) { + super(world, thrower, damageMultiplier); + this.blastMultiplier = blastMultiplier; + } + + public EntityBomb(World par1World, double par2, double par4, double par6) { + super(par1World, par2, par4, par6); + } + + @Override + public void writeSpawnData(ByteBuf buffer) { + buffer.writeFloat(blastMultiplier); + } + + @Override + public void readSpawnData(ByteBuf buffer) { + blastMultiplier = buffer.readFloat(); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + blastMultiplier = nbttagcompound.getFloat("blastMultiplier"); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + nbttagcompound.setFloat("blastMultiplier", blastMultiplier); + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityDarknessOrb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityDarknessOrb.java new file mode 100644 index 00000000..1ba45a28 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityDarknessOrb.java @@ -0,0 +1,88 @@ +package electroblob.wizardry.entity.projectile; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.MobEffects; +import net.minecraft.init.SoundEvents; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class EntityDarknessOrb extends EntityMagicProjectile +{ + public EntityDarknessOrb(World par1World) + { + super(par1World); + } + + public EntityDarknessOrb(World par1World, EntityLivingBase par2EntityLivingBase) + { + super(par1World, par2EntityLivingBase); + } + + public EntityDarknessOrb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier) + { + super(par1World, par2EntityLivingBase, damageMultiplier); + } + + public EntityDarknessOrb(World par1World, double par2, double par4, double par6) + { + super(par1World, par2, par4, par6); + } + + @Override + protected float getSpeed(){ + return 0.5F; + } + + @Override + protected void onImpact(RayTraceResult RayTraceResult) + { + Entity target = RayTraceResult.entityHit; + + if (target != null && !MagicDamage.isEntityImmune(DamageType.WITHER, target)) + { + float damage = 8 * damageMultiplier; + + target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.WITHER).setProjectile(), damage); + + if(target instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.WITHER, target)) ((EntityLivingBase)target).addPotionEffect(new PotionEffect(MobEffects.WITHER, 150, 1)); + + this.playSound(SoundEvents.ENTITY_WITHER_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + } + + this.setDead(); + } + + public void onUpdate(){ + + super.onUpdate(); + + if(worldObj.isRemote){ + float brightness = rand.nextFloat()*0.2f; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0, 20 + rand.nextInt(10), brightness, 0.0f, brightness); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + } + + if(this.ticksExisted > 150){ + this.setDead(); + } + + // Cancels out the slowdown effect in EntityThrowable + this.motionX /= 0.99; + this.motionY /= 0.99; + this.motionZ /= 0.99; + } + + /** + * Gets the amount of gravity to apply to the thrown entity with each tick. + */ + protected float getGravityVelocity() + { + return 0.0F; + } +} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityDart.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityDart.java new file mode 100644 index 00000000..3625f995 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityDart.java @@ -0,0 +1,95 @@ +package electroblob.wizardry.entity.projectile; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.MobEffects; +import net.minecraft.init.SoundEvents; +import net.minecraft.potion.PotionEffect; +import net.minecraft.world.World; + +public class EntityDart extends EntityMagicArrow +{ + /** Basic shell constructor. Should only be used by the client. */ + public EntityDart(World world) { + super(world); + } + + /** Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this + * constructor and then call setVelocity() as that method is, bizarrely, client-side only. */ + public EntityDart(World world, double x, double y, double z) + { + super(world, x, y, z); + } + + /** Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be altered + * slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on easy, 6 on + * normal and 2 on hard difficulty. */ + public EntityDart(World world, EntityLivingBase caster, Entity target, float speed, float aimingError, float damageMultiplier) + { + super(world, caster, target, speed, aimingError, damageMultiplier); + } + + /** Creates a projectile pointing in the direction the caster is looking, with the given speed. + * USE THIS CONSTRUCTOR FOR NORMAL SPELLS. */ + public EntityDart(World world, EntityLivingBase caster, float speed, float damageMultiplier) + { + super(world, caster, speed, damageMultiplier); + } + + @Override + public void onEntityHit(EntityLivingBase entityHit){ + //Adds a weakness effect to the target. + entityHit.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, 200, 1, false, false)); + this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + } + + @Override + public void onBlockHit(){ + this.playSound(SoundEvents.ENTITY_ARROW_HIT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + } + + @Override + public void tickInAir(){ + + if(this.worldObj.isRemote){ + Wizardry.proxy.spawnParticle(WizardryParticleType.LEAF, worldObj, this.posX, this.posY, this.posZ, 0, -0.03, 0, 10 + rand.nextInt(5)); + } + } + + // Replicates the original behaviour of staying stuck in block for a few seconds before disappearing. + @Override + public void tickInGround(){ + if(this.ticksInGround > 60){ + this.setDead(); + } + } + + @Override + public double getDamage(){ + return 4.0d; + } + + @Override + public DamageType getDamageType(){ + return DamageType.MAGIC; + } + + @Override + public boolean doGravity(){ + return true; + } + + @Override + public boolean doDeceleration(){ + return true; + } + + @Override + protected void entityInit() { + + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityFirebolt.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityFirebolt.java new file mode 100644 index 00000000..ed8c5c0f --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityFirebolt.java @@ -0,0 +1,93 @@ +package electroblob.wizardry.entity.projectile; + +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.SoundEvents; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class EntityFirebolt extends EntityMagicProjectile +{ + public EntityFirebolt(World par1World) + { + super(par1World); + } + + public EntityFirebolt(World par1World, EntityLivingBase par2EntityLivingBase) + { + super(par1World, par2EntityLivingBase); + } + + public EntityFirebolt(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier) + { + super(par1World, par2EntityLivingBase, damageMultiplier); + } + + public EntityFirebolt(World par1World, double par2, double par4, double par6) + { + super(par1World, par2, par4, par6); + } + + @Override + protected void onImpact(RayTraceResult rayTrace) + { + Entity entityHit = rayTrace.entityHit; + + if (entityHit != null) + { + float damage = 5 * damageMultiplier; + + entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(), damage); + + if(!MagicDamage.isEntityImmune(DamageType.FIRE, entityHit)) entityHit.setFire(5); + } + + this.playSound(SoundEvents.BLOCK_LAVA_POP, 2, 0.8f + rand.nextFloat()*0.3f); + + // Particle effect + if(worldObj.isRemote){ + for(int i=0;i<8;i++){ + worldObj.spawnParticle(EnumParticleTypes.LAVA, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0); + } + } + + this.setDead(); + } + + @Override + public void onUpdate(){ + + super.onUpdate(); + + if(worldObj.isRemote){ + for(int i=0; i<4; i++){ + worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + rand.nextFloat()*0.2 - 0.1, this.posY + this.height/2 + rand.nextFloat()*0.2 - 0.1, this.posZ + rand.nextFloat()*0.2 - 0.1, 0, 0, 0); + } + } + + if(this.ticksExisted > 8){ + this.setDead(); + } + } + + /** + * Gets the amount of gravity to apply to the thrown entity with each tick. + */ + @Override + protected float getGravityVelocity() + { + return 0.0F; + } + + /** + * Return whether this entity should be rendered as on fire. + */ + @Override + public boolean canRenderOnFire() + { + return false; + } +} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityFirebomb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityFirebomb.java new file mode 100644 index 00000000..637fe5e8 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityFirebomb.java @@ -0,0 +1,87 @@ +package electroblob.wizardry.entity.projectile; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.SoundEvents; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class EntityFirebomb extends EntityBomb { + + public EntityFirebomb(World par1World) + { + super(par1World); + } + + public EntityFirebomb(World par1World, EntityLivingBase par2EntityLivingBase) + { + super(par1World, par2EntityLivingBase); + } + + public EntityFirebomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier) + { + super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier); + } + + public EntityFirebomb(World par1World, double par2, double par4, double par6) + { + super(par1World, par2, par4, par6); + } + + /** + * Called when this EntityThrowable hits a block or entity. + */ + protected void onImpact(RayTraceResult par1RayTraceResult) + { + Entity entityHit = par1RayTraceResult.entityHit; + + if (entityHit != null) + { + // This is if the firebomb gets a direct hit + float damage = 5 * damageMultiplier; + + entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(), damage); + + if(!MagicDamage.isEntityImmune(DamageType.FIRE, entityHit)) entityHit.setFire(10); + } + + // Particle effect + if(worldObj.isRemote){ + this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0); + for(int i=0;i<60*blastMultiplier;i++){ + //this.worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0, 0, 0); + Wizardry.proxy.spawnParticle(WizardryParticleType.MAGIC_FIRE, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0, 0, 0, 15 + rand.nextInt(5), 2 + rand.nextFloat(), 0, 0); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, 1.0f, 0.2f + rand.nextFloat()*0.4f, 0.0f); + } + } + + if(!this.worldObj.isRemote){ + + this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5F, rand.nextFloat() * 0.4F + 0.6F); + this.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1); + + double range = 3.0d*blastMultiplier; + + List targets = WizardryUtilities.getEntitiesWithinRadius(range, this.posX, this.posY, this.posZ, this.worldObj); + + for(EntityLivingBase target : targets){ + if(target != entityHit && target != this.getThrower() && !MagicDamage.isEntityImmune(DamageType.FIRE, target)){ + // Splash damage does not count as projectile damage + target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE), 4.0f * damageMultiplier); + target.setFire(7); + } + } + + this.setDead(); + } + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityForceArrow.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityForceArrow.java new file mode 100644 index 00000000..0cf2ec6d --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityForceArrow.java @@ -0,0 +1,85 @@ +package electroblob.wizardry.entity.projectile; + +import electroblob.wizardry.util.MagicDamage.DamageType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.SoundEvents; +import net.minecraft.world.World; + +public class EntityForceArrow extends EntityMagicArrow { + + /** Basic shell constructor. Should only be used by the client. */ + public EntityForceArrow(World world) { + super(world); + } + + /** Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this + * constructor and then call setVelocity() as that method is, bizarrely, client-side only. */ + public EntityForceArrow(World world, double x, double y, double z) + { + super(world, x, y, z); + } + + /** Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be altered + * slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on easy, 6 on + * normal and 2 on hard difficulty. */ + public EntityForceArrow(World world, EntityLivingBase caster, Entity target, float speed, float aimingError, float damageMultiplier) + { + super(world, caster, target, speed, aimingError, damageMultiplier); + } + + /** Creates a projectile pointing in the direction the caster is looking, with the given speed. + * USE THIS CONSTRUCTOR FOR NORMAL SPELLS. */ + public EntityForceArrow(World world, EntityLivingBase caster, float speed, float damageMultiplier) + { + super(world, caster, speed, damageMultiplier); + } + + @Override + public void onEntityHit(EntityLivingBase entityHit){ + this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.0F, 1.0F); + } + + @Override + public void tickInGround(){ + this.setDead(); + } + + @Override + public void onBlockHit(){ + this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.0F, 1.0F); + } + + @Override + public void tickInAir(){ + if (this.ticksExisted > 20){ + this.setDead(); + } + } + + @Override + public double getDamage(){ + return 7.0d; + } + + @Override + public DamageType getDamageType(){ + return DamageType.FORCE; + } + + @Override + public boolean doGravity(){ + return false; + } + + @Override + public boolean doDeceleration(){ + return false; + } + + @Override + protected void entityInit() { + + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityForceOrb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityForceOrb.java new file mode 100644 index 00000000..9a4eaa7e --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityForceOrb.java @@ -0,0 +1,111 @@ +package electroblob.wizardry.entity.projectile; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.SoundEvents; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class EntityForceOrb extends EntityMagicProjectile { + + /** The entity blast multiplier. In this particular case, it doesn't need syncing, so this class doesn't extend + * EntityBlastProjectile. */ + public float blastMultiplier; + + public EntityForceOrb(World par1World) + { + super(par1World); + } + + public EntityForceOrb(World par1World, EntityLivingBase par2EntityLivingBase) + { + super(par1World, par2EntityLivingBase); + } + + public EntityForceOrb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier) + { + super(par1World, par2EntityLivingBase, damageMultiplier); + this.blastMultiplier = blastMultiplier; + } + + public EntityForceOrb(World par1World, double par2, double par4, double par6) + { + super(par1World, par2, par4, par6); + } + + /** + * Called when this EntityThrowable hits a block or entity. + */ + protected void onImpact(RayTraceResult par1RayTraceResult){ + + if (par1RayTraceResult.entityHit != null) + { + // This is if the force orb gets a direct hit + this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + } + + // Particle effect + if(this.worldObj.isRemote){ + for(int j=0; j<20; j++){ + float brightness = 0.5f + (rand.nextFloat()/2); + double x = this.posX - 0.25d + (rand.nextDouble()/2); + double y = this.posY - 0.25d + (rand.nextDouble()/2); + double z = this.posZ - 0.25d + (rand.nextDouble()/2); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, x, y, z, (x - this.posX)*2, (y - this.posY)*2, (z - this.posZ)*2, 6, brightness, 1.0f, brightness + 0.2f); + } + this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0); + } + + if(!this.worldObj.isRemote){ + + // 2 gives a cool flanging effect! + float pitch = this.rand.nextFloat() * 0.2F + 0.3F; + this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.5F, pitch); + this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.5F, pitch - 0.01f); + + double blastRadius = 4.0d*blastMultiplier; + + List targets = WizardryUtilities.getEntitiesWithinRadius(blastRadius, this.posX, this.posY, this.posZ, this.worldObj); + + for(EntityLivingBase target : targets){ + if(target != this.getThrower()){ + + double velY = target.motionY; + + double dx = this.posX-target.posX > 0? -0.5 - (this.posX-target.posX)/8 : 0.5 - (this.posX-target.posX)/8; + double dz = this.posZ-target.posZ > 0? -0.5 - (this.posZ-target.posZ)/8 : 0.5 - (this.posZ-target.posZ)/8; + + float damage = 4*damageMultiplier; + + target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.BLAST), damage); + + target.motionX = dx; + target.motionY = velY + 0.4; + target.motionZ = dz; + } + } + + this.setDead(); + } + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + blastMultiplier = nbttagcompound.getFloat("blastMultiplier"); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + nbttagcompound.setFloat("blastMultiplier", blastMultiplier); + } +} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityIceCharge.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityIceCharge.java new file mode 100644 index 00000000..ce5c9907 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityIceCharge.java @@ -0,0 +1,135 @@ +package electroblob.wizardry.entity.projectile; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.Blocks; +import net.minecraft.init.SoundEvents; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class EntityIceCharge extends EntityBomb { + + public EntityIceCharge(World par1World) + { + super(par1World); + } + + public EntityIceCharge(World par1World, EntityLivingBase par2EntityLivingBase) + { + super(par1World, par2EntityLivingBase); + } + + public EntityIceCharge(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier) + { + super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier); + } + + public EntityIceCharge(World par1World, double par2, double par4, double par6) + { + super(par1World, par2, par4, par6); + } + + /** + * Called when this EntityThrowable hits a block or entity. + */ + protected void onImpact(RayTraceResult par1RayTraceResult) + { + Entity entityHit = par1RayTraceResult.entityHit; + + if (entityHit != null) + { + // This is if the ice charge gets a direct hit + float damage = 4 * damageMultiplier; + + entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FROST).setProjectile(), damage); + + if(entityHit instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.FROST, entityHit)) + ((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(WizardryPotions.frost, 120, 1)); + } + + // Particle effect + if(worldObj.isRemote){ + this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0); + for(int i=0;i<30*blastMultiplier;i++){ + float brightness = 0.4f + rand.nextFloat()*0.5f; + Wizardry.proxy.spawnParticle(WizardryParticleType.ICE, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 35); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, brightness, brightness+0.1f, 1.0f); + } + } + + if(!this.worldObj.isRemote){ + + this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5f, rand.nextFloat() * 0.4f + 0.6f); + this.playSound(WizardrySounds.SPELL_ICE, 1.2f, rand.nextFloat() * 0.4f + 1.2f); + + double radius = 3.0d*blastMultiplier; + + List targets = WizardryUtilities.getEntitiesWithinRadius(radius, this.posX, this.posY, this.posZ, this.worldObj); + + // Slows targets + for(EntityLivingBase target : targets){ + if(target != entityHit && target != this.getThrower()){ + if(!MagicDamage.isEntityImmune(DamageType.FROST, target)) target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 100, 0)); + } + } + + // Places snow and ice on ground. + for(int i=-1; i<2; i++){ + for(int j=-1; j<2; j++){ + + BlockPos pos = new BlockPos(this.posX + i, this.posY, this.posZ + j); + + int y = WizardryUtilities.getNearestFloorLevelB(worldObj, pos, 7); + + pos = new BlockPos(pos.getX(), y, pos.getZ()); + + double dist = this.getDistance(pos.getX(), pos.getY(), pos.getZ()); + + // Randomised with weighting so that the nearer the block the more likely it is to be snowed. + if(y != -1 && rand.nextInt((int)dist*2 + 1) < 1 && dist < 2){ + if(worldObj.getBlockState(pos.down()).getBlock() == Blocks.WATER){ + worldObj.setBlockState(pos.down(), Blocks.ICE.getDefaultState()); + }else{ + // Don't need to check whether the block at pos can be replaced since getNearestFloorLevelB + // only ever returns floors with air above them. + worldObj.setBlockState(pos, Blocks.SNOW_LAYER.getDefaultState()); + } + } + } + } + + // Releases shards + for(int i=0; i<10; i++){ + double dx = rand.nextDouble()-0.5; + double dy = rand.nextDouble()-0.5; + double dz = rand.nextDouble()-0.5; + EntityIceShard iceshard = new EntityIceShard(worldObj, this.posX + dx, this.posY + dy, this.posZ + dz); + iceshard.motionX = dx; + iceshard.motionY = dy; + iceshard.motionZ = dz; + iceshard.setShootingEntity(this.getThrower()); + iceshard.damageMultiplier = this.damageMultiplier; + worldObj.spawnEntityInWorld(iceshard); + } + + this.setDead(); + } + } + + @Override + public boolean canRenderOnFire() { + return false; + } +} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityIceLance.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityIceLance.java new file mode 100644 index 00000000..ca4dd674 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityIceLance.java @@ -0,0 +1,117 @@ +package electroblob.wizardry.entity.projectile; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.SoundEvents; +import net.minecraft.potion.PotionEffect; +import net.minecraft.world.World; + +public class EntityIceLance extends EntityMagicArrow { + + /** Basic shell constructor. Should only be used by the client. */ + public EntityIceLance(World world) { + super(world); + this.setKnockbackStrength(1); + } + + /** Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this + * constructor and then call setVelocity() as that method is, bizarrely, client-side only. */ + public EntityIceLance(World world, double x, double y, double z) + { + super(world, x, y, z); + this.setKnockbackStrength(1); + } + + /** Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be altered + * slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on easy, 6 on + * normal and 2 on hard difficulty. */ + public EntityIceLance(World world, EntityLivingBase caster, Entity target, float speed, float aimingError, float damageMultiplier) + { + super(world, caster, target, speed, aimingError, damageMultiplier); + this.setKnockbackStrength(1); + } + + /** Creates a projectile pointing in the direction the caster is looking, with the given speed. + * USE THIS CONSTRUCTOR FOR NORMAL SPELLS. */ + public EntityIceLance(World world, EntityLivingBase caster, float speed, float damageMultiplier) + { + super(world, caster, speed, damageMultiplier); + this.setKnockbackStrength(1); + } + + @Override + public void onEntityHit(EntityLivingBase entityHit){ + + // Adds a freeze effect to the target. + if(!MagicDamage.isEntityImmune(DamageType.FROST, entityHit)) entityHit.addPotionEffect(new PotionEffect(WizardryPotions.frost, 300, 0)); + + this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + } + + @Override + public void tickInAir(){ + + } + + @Override + public void onBlockHit(){ + // Adds a particle effect when the ice lance hits a block. + if(this.worldObj.isRemote){ + for(int j=0; j<10; j++){ + double x = this.posX - 0.25d + (rand.nextDouble()/2); + double y = this.posY - 0.25d + (rand.nextDouble()/2); + double z = this.posZ - 0.25d + (rand.nextDouble()/2); + Wizardry.proxy.spawnParticle(WizardryParticleType.ICE, worldObj, x, y, z, x - this.posX, y - this.posY, z - this.posZ, 20 + rand.nextInt(10)); + } + } + // Parameters for sound: sound event name, volume, pitch. + this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.0F, rand.nextFloat() * 0.4F + 1.2F); + + } + + @Override + public void tickInGround(){ + this.setDead(); + } + + @Override + public double getDamage(){ + return 10.0d; + } + + @Override + public DamageType getDamageType(){ + return DamageType.FROST; + } + + @Override + public boolean doGravity(){ + return true; + } + + @Override + public boolean doDeceleration(){ + return true; + } + + @Override + public boolean doOverpenetration(){ + return true; + } + + @Override + protected void entityInit() { + + } + + @Override + public boolean canRenderOnFire() { + return false; + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityIceShard.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityIceShard.java new file mode 100644 index 00000000..0805c4e0 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityIceShard.java @@ -0,0 +1,103 @@ +package electroblob.wizardry.entity.projectile; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.SoundEvents; +import net.minecraft.potion.PotionEffect; +import net.minecraft.world.World; + +public class EntityIceShard extends EntityMagicArrow { + + /** Basic shell constructor. Should only be used by the client. */ + public EntityIceShard(World world) { + super(world); + } + + /** Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this + * constructor and then call setVelocity() as that method is, bizarrely, client-side only. */ + public EntityIceShard(World world, double x, double y, double z) + { + super(world, x, y, z); + } + + /** Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be altered + * slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on easy, 6 on + * normal and 2 on hard difficulty. */ + public EntityIceShard(World world, EntityLivingBase caster, Entity target, float speed, float aimingError, float damageMultiplier) + { + super(world, caster, target, speed, aimingError, damageMultiplier); + } + + /** Creates a projectile pointing in the direction the caster is looking, with the given speed. + * USE THIS CONSTRUCTOR FOR NORMAL SPELLS. */ + public EntityIceShard(World world, EntityLivingBase caster, float speed, float damageMultiplier) + { + super(world, caster, speed, damageMultiplier); + } + + @Override + public void onEntityHit(EntityLivingBase entityHit){ + + // Adds a freeze effect to the target. + if(!MagicDamage.isEntityImmune(DamageType.FROST, entityHit)) entityHit.addPotionEffect(new PotionEffect(WizardryPotions.frost, 200, 0)); + + this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + } + + @Override + public void tickInAir(){ + + } + + @Override + public void onBlockHit(){ + // Adds a particle effect when the ice shard hits a block. + if(this.worldObj.isRemote){ + for(int j=0; j<10; j++){ + double x = this.posX - 0.25d + (rand.nextDouble()/2); + double y = this.posY - 0.25d + (rand.nextDouble()/2); + double z = this.posZ - 0.25d + (rand.nextDouble()/2); + Wizardry.proxy.spawnParticle(WizardryParticleType.ICE, worldObj, x, y, z, x - this.posX, y - this.posY, z - this.posZ, 20 + rand.nextInt(10)); + } + } + // Parameters for sound: sound event name, volume, pitch. + this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.0F, rand.nextFloat() * 0.4F + 1.2F); + + } + + @Override + public double getDamage(){ + return 6.0d; + } + + @Override + public DamageType getDamageType(){ + return DamageType.FROST; + } + + @Override + public boolean doGravity(){ + return true; + } + + @Override + public boolean doDeceleration(){ + return true; + } + + @Override + protected void entityInit() { + + } + + @Override + public boolean canRenderOnFire() { + return false; + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityLightningArrow.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityLightningArrow.java new file mode 100644 index 00000000..e7f250ba --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityLightningArrow.java @@ -0,0 +1,95 @@ +package electroblob.wizardry.entity.projectile; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.world.World; + +public class EntityLightningArrow extends EntityMagicArrow { + + /** Basic shell constructor. Should only be used by the client. */ + public EntityLightningArrow(World world) { + super(world); + } + + /** Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this + * constructor and then call setVelocity() as that method is, bizarrely, client-side only. */ + public EntityLightningArrow(World world, double x, double y, double z) + { + super(world, x, y, z); + } + + /** Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be altered + * slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on easy, 6 on + * normal and 2 on hard difficulty. */ + public EntityLightningArrow(World world, EntityLivingBase caster, Entity target, float speed, float aimingError, float damageMultiplier) + { + super(world, caster, target, speed, aimingError, damageMultiplier); + } + + /** Creates a projectile pointing in the direction the caster is looking, with the given speed. + * USE THIS CONSTRUCTOR FOR NORMAL SPELLS. */ + public EntityLightningArrow(World world, EntityLivingBase caster, float speed, float damageMultiplier) + { + super(world, caster, speed, damageMultiplier); + } + + @Override + public void onEntityHit(EntityLivingBase entityHit){ + + if(worldObj.isRemote){ + for(int j=0;j<8;j++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); + } + } + /* Pretty sure this needn't be here, probably missed it when I implemented the damage type system. + if(entityHit instanceof EntityCreeper && !((EntityCreeper)entityHit).getPowered()){ + entityHit.getDataWatcher().updateObject(17, Byte.valueOf((byte)1)); + if(this.getShootingEntity() instanceof EntityPlayer) ((EntityPlayer)this.getShootingEntity()).addStat(Wizardry.chargeCreeper); + } + */ + this.playSound(WizardrySounds.SPELL_SPARK, 1.0F, 1.0F); + } + + @Override + public void tickInAir(){ + + if (this.ticksExisted > 20){ + this.setDead(); + } + + if(worldObj.isRemote){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX, this.posY, this.posZ, 0, 0, 0, 3); + } + + } + + @Override + public double getDamage(){ + return 7.0d; + } + + @Override + public DamageType getDamageType(){ + return DamageType.SHOCK; + } + + @Override + public boolean doGravity(){ + return false; + } + + @Override + public boolean doDeceleration(){ + return false; + } + + @Override + protected void entityInit() { + + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityLightningDisc.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityLightningDisc.java new file mode 100644 index 00000000..e27e34d1 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityLightningDisc.java @@ -0,0 +1,121 @@ +package electroblob.wizardry.entity.projectile; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class EntityLightningDisc extends EntityMagicProjectile +{ + public EntityLightningDisc(World par1World) + { + super(par1World); + } + + public EntityLightningDisc(World par1World, EntityLivingBase par2EntityLivingBase) + { + super(par1World, par2EntityLivingBase); + } + + public EntityLightningDisc(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier) + { + super(par1World, par2EntityLivingBase, damageMultiplier); + } + + public EntityLightningDisc(World par1World, double par2, double par4, double par6) + { + super(par1World, par2, par4, par6); + this.width = 2.0f; + this.height = 0.5f; + } + + @Override + protected float getSpeed(){ + return 1.2f; + } + + + @Override + protected void onImpact(RayTraceResult mop) + { + Entity entityHit = mop.entityHit; + + if (entityHit != null) + { + float damage = 12 * damageMultiplier; + + entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK), damage); + } + + this.playSound(WizardrySounds.SPELL_SPARK, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + + if(mop.typeOfHit == RayTraceResult.Type.BLOCK) this.setDead(); + } + + @Override + public void onUpdate(){ + + super.onUpdate(); + + // Particle effect + if(worldObj.isRemote){ + for(int i=0;i<8;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + rand.nextFloat()*2 - 1, this.posY, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); + //worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0); + } + } + + if(!this.isCollided && !worldObj.isRemote){ + + double seekingRange = 5.0d; + + List entities = WizardryUtilities.getEntitiesWithinRadius(seekingRange, this.posX, this.posY, this.posZ, this.worldObj); + Entity target = null; + + for(Entity possibleTarget : entities){ + // Decides if current entity should be replaced. + if(target == null || this.getDistanceToEntity(target) > this.getDistanceToEntity(possibleTarget)){ + // Decides if new entity is a valid target. + if(WizardryUtilities.isValidTarget(this.getThrower(), possibleTarget)){ + target = possibleTarget; + } + } + } + + if(target != null && Math.abs(this.motionX) < 1 && Math.abs(this.motionY) < 1 && Math.abs(this.motionZ) < 1){ + this.addVelocity((target.posX - this.posX)/30, (target.posY + target.height/2 - this.posY)/30, (target.posZ - this.posZ)/30); + } + } + + if(this.ticksExisted > 50){ + this.setDead(); + } + + // Cancels out the slowdown effect in EntityThrowable + this.motionX /= 0.99; + this.motionY /= 0.99; + this.motionZ /= 0.99; + } + + + @Override + protected float getGravityVelocity() + { + return 0.0F; + } + + + @Override + public boolean canRenderOnFire() + { + return false; + } +} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicArrow.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicArrow.java new file mode 100644 index 00000000..fefa58cd --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicArrow.java @@ -0,0 +1,582 @@ +package electroblob.wizardry.entity.projectile; + +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.UUID; + +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryUtilities; +import io.netty.buffer.ByteBuf; +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.IProjectile; +import net.minecraft.entity.monster.EntityEnderman; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.play.server.SPacketChangeGameState; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumParticleTypes; +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.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** This class was copied from EntityArrow in the 1.7.10 update as part of the overhaul and major cleanup + * of the code for the projectiles. It provides a unifying superclass for all directed projectiles (i.e. not + * spherical stuff like snowballs), namely magic missile, ice shard, force arrow, lightning arrow and dart. + * All spherical projectiles should extend {@link EntityMagicProjectile}. + *

    + * This class handles saving of the damage multiplier and all shared logic. Methods are provided which are + * triggered at useful points during the entity update cycle as well as a few getters for various properties. + * Override any of these to change the behaviour (no need to call super for any of them). + * @since Wizardry 1.0 + * @author Electroblob + */ +public abstract class EntityMagicArrow extends Entity implements IProjectile, IEntityAdditionalSpawnData { + + private int blockX = -1; + private int blockY = -1; + private int blockZ = -1; + /** The block the arrow is stuck in */ + private IBlockState stuckInBlock; + /** The metadata of the block the arrow is stuck in */ + private int inData; + private boolean inGround; + /** Seems to be some sort of timer for animating an arrow. */ + public int arrowShake; + /** The owner of this arrow. */ + private WeakReference shootingEntity; + /** The UUID of the caster. Note that this is only for loading purposes; during normal updates + * the actual entity instance is stored (so that getEntityByUUID is not called constantly), + * so this will not always be synced (this is why it is private). */ + private UUID casterUUID; + int ticksInGround; + int ticksInAir; + /** The amount of knockback an arrow applies when it hits a mob. */ + private int knockbackStrength; + /** The damage multiplier for the arrow. Normally this isn't set directly, since it can be done via the + * constructor. An exception is where other entities need to pass in their multipliers, e.g. ice charge. */ + public float damageMultiplier = 1.0f; + + /** Basic shell constructor. Should only be used by the client. */ + public EntityMagicArrow(World world) + { + super(world); + this.setSize(0.5F, 0.5F); + } + + /** Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this + * constructor and then call setVelocity() as that method is, bizarrely, client-side only. */ + public EntityMagicArrow(World world, double x, double y, double z) + { + super(world); + this.setSize(0.5F, 0.5F); + this.setPosition(x, y, z); + // yOffset was set to 0 here, but that has been replaced by getYOffset(), which returns 0 in Entity anyway. + } + + /** Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be altered + * slightly by a random amount determined by the aimingError parameter. For reference, skeletons set this to 10 on easy, 6 on + * normal and 2 on hard difficulty. */ + public EntityMagicArrow(World world, EntityLivingBase caster, Entity target, float speed, float aimingError, float damageMultiplier) + { + super(world); + this.shootingEntity = new WeakReference(caster); + this.damageMultiplier = damageMultiplier; + + this.posY = caster.posY + (double)caster.getEyeHeight() - 0.10000000149011612D; + double d0 = target.posX - caster.posX; + double d1 = this.doGravity() ? target.getEntityBoundingBox().minY + (double)(target.height / 3.0F) - this.posY + : target.getEntityBoundingBox().minY + (double)(target.height / 2.0F) - this.posY; + double d2 = target.posZ - caster.posZ; + double d3 = (double)MathHelper.sqrt_double(d0 * d0 + d2 * d2); + + if (d3 >= 1.0E-7D) + { + float f2 = (float)(Math.atan2(d2, d0) * 180.0D / Math.PI) - 90.0F; + float f3 = (float)(-(Math.atan2(d1, d3) * 180.0D / Math.PI)); + double d4 = d0 / d3; + double d5 = d2 / d3; + this.setLocationAndAngles(caster.posX + d4, this.posY, caster.posZ + d5, f2, f3); + // yOffset was set to 0 here, but that has been replaced by getYOffset(), which returns 0 in Entity anyway. + + // f4 depends on the horizontal distance between the two entities and accounts for bullet drop, + // but of course if gravity is ignored this should be 0. + float bulletDropCompensation = this.doGravity() ? (float)d3 * 0.2F : 0; + this.setThrowableHeading(d0, d1 + (double)bulletDropCompensation, d2, speed, aimingError); + } + } + + /** Creates a projectile pointing in the direction the caster is looking, with the given speed. + * Use this constructor for normal, player-cast spells. */ + public EntityMagicArrow(World world, EntityLivingBase caster, float speed, float damageMultiplier) + { + super(world); + this.shootingEntity = new WeakReference(caster); + this.damageMultiplier = damageMultiplier; + + this.setSize(0.5F, 0.5F); + this.setLocationAndAngles(caster.posX, caster.posY + (double)caster.getEyeHeight(), caster.posZ, caster.rotationYaw, caster.rotationPitch); + this.posX -= (double)(MathHelper.cos(this.rotationYaw / 180.0F * (float)Math.PI) * 0.16F); + this.posY -= 0.10000000149011612D; + this.posZ -= (double)(MathHelper.sin(this.rotationYaw / 180.0F * (float)Math.PI) * 0.16F); + this.setPosition(this.posX, this.posY, this.posZ); + // yOffset was set to 0 here, but that has been replaced by getYOffset(), which returns 0 in Entity anyway. + this.motionX = (double)(-MathHelper.sin(this.rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float)Math.PI)); + this.motionZ = (double)(MathHelper.cos(this.rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float)Math.PI)); + this.motionY = (double)(-MathHelper.sin(this.rotationPitch / 180.0F * (float)Math.PI)); + this.setThrowableHeading(this.motionX, this.motionY, this.motionZ, speed * 1.5F, 1.0F); + } + + /** Subclasses must override this to set their own base damage. */ + public abstract double getDamage(); + + /** Override this to specify the damage type dealt. Defaults to {@link DamageType#MAGIC}. */ + public DamageType getDamageType(){ + return DamageType.MAGIC; + } + + /** Override this to disable gravity. Returns true by default. */ + public boolean doGravity(){ + return true; + } + + /** Override this to disable deceleration (generally speaking, this isn't noticeable unless gravity + * is turned off). Returns true by default. */ + public boolean doDeceleration(){ + return true; + } + + /** Override this to allow the projectile to pass through mobs intact (the onEntityHit method will + * still be called and damage will still be applied). Returns false by default. */ + public boolean doOverpenetration(){ + return false; + } + + /** + * Similar to setArrowHeading, it's point the throwable entity to a x, y, z direction. + */ + public void setThrowableHeading(double x, double y, double z, float speed, float randomness) + { + float f2 = MathHelper.sqrt_double(x * x + y * y + z * z); + x /= (double)f2; + y /= (double)f2; + z /= (double)f2; + x += this.rand.nextGaussian() * (double)(this.rand.nextBoolean() ? -1 : 1) * 0.007499999832361937D * (double)randomness; + y += this.rand.nextGaussian() * (double)(this.rand.nextBoolean() ? -1 : 1) * 0.007499999832361937D * (double)randomness; + z += this.rand.nextGaussian() * (double)(this.rand.nextBoolean() ? -1 : 1) * 0.007499999832361937D * (double)randomness; + x *= (double)speed; + y *= (double)speed; + z *= (double)speed; + this.motionX = x; + this.motionY = y; + this.motionZ = z; + float f3 = MathHelper.sqrt_double(x * x + z * z); + this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(x, z) * 180.0D / Math.PI); + this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(y, (double)f3) * 180.0D / Math.PI); + this.ticksInGround = 0; + } + + // There was an override for setPositionAndRotationDirect here, but it was exactly the same as the superclass + // method (in Entity), so it was removed since it was redundant. + + /** + * Sets the velocity to the args. Args: x, y, z. THIS IS CLIENT SIDE ONLY! DO NOT USE IN COMMON OR SERVER CODE! + */ + @Override + @SideOnly(Side.CLIENT) + public void setVelocity(double p_70016_1_, double p_70016_3_, double p_70016_5_) + { + this.motionX = p_70016_1_; + this.motionY = p_70016_3_; + this.motionZ = p_70016_5_; + + if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F) + { + float f = MathHelper.sqrt_double(p_70016_1_ * p_70016_1_ + p_70016_5_ * p_70016_5_); + this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(p_70016_1_, p_70016_5_) * 180.0D / Math.PI); + this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(p_70016_3_, (double)f) * 180.0D / Math.PI); + this.prevRotationPitch = this.rotationPitch; + this.prevRotationYaw = this.rotationYaw; + this.setLocationAndAngles(this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch); + this.ticksInGround = 0; + } + } + + /** Called each tick when the projectile is in a block. Defaults to setDead(), but can be overridden to + * change the behaviour. */ + public void tickInGround(){ + this.setDead(); + } + + /** Called each tick when the projectile is in the air. Override to add particles and such like. */ + public void tickInAir(){} + + /** Called when the projectile hits an entity. Override to add potion effects and such like. */ + public void onEntityHit(EntityLivingBase entityHit){} + + /** Called when the projectile hits a block. Override to add sound effects and such like. */ + public void onBlockHit(){} + + @Override + public void onUpdate(){ + + super.onUpdate(); + + if(this.getShootingEntity() == null && this.casterUUID != null){ + Entity entity = WizardryUtilities.getEntityByUUID(worldObj, casterUUID); + if(entity instanceof EntityLivingBase){ + this.shootingEntity = new WeakReference((EntityLivingBase)entity); + } + } + + if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F) + { + float f = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); + this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI); + this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(this.motionY, (double)f) * 180.0D / Math.PI); + } + + BlockPos blockpos = new BlockPos(this.blockX, this.blockY, this.blockZ); + IBlockState iblockstate = this.worldObj.getBlockState(blockpos); + + if (iblockstate.getMaterial() != Material.AIR) + { + AxisAlignedBB axisalignedbb = iblockstate.getCollisionBoundingBox(this.worldObj, blockpos); + + if (axisalignedbb != Block.NULL_AABB && axisalignedbb.offset(blockpos).isVecInside(new Vec3d(this.posX, this.posY, this.posZ))) + { + this.inGround = true; + } + } + + if (this.arrowShake > 0) + { + --this.arrowShake; + } + + // When the arrow is in the ground + if (this.inGround) + { + ++this.ticksInGround; + this.tickInGround(); + } + // When the arrow is in the air + else{ + + this.tickInAir(); + + this.ticksInGround = 0; + ++this.ticksInAir; + Vec3d vec3d1 = new Vec3d(this.posX, this.posY, this.posZ); + Vec3d vec3d = new Vec3d(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); + RayTraceResult raytraceresult = this.worldObj.rayTraceBlocks(vec3d1, vec3d, false, true, false); + vec3d1 = new Vec3d(this.posX, this.posY, this.posZ); + vec3d = new Vec3d(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); + + if (raytraceresult != null) + { + vec3d = new Vec3d(raytraceresult.hitVec.xCoord, raytraceresult.hitVec.yCoord, raytraceresult.hitVec.zCoord); + } + + + Entity entity = null; + List list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox().addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D)); + double d0 = 0.0D; + int i; + float f1; + + for (i = 0; i < list.size(); ++i) + { + Entity entity1 = (Entity)list.get(i); + + if (entity1.canBeCollidedWith() && (entity1 != this.getShootingEntity() || this.ticksInAir >= 5)) + { + f1 = 0.3F; + AxisAlignedBB axisalignedbb1 = entity1.getEntityBoundingBox().expand((double)f1, (double)f1, (double)f1); + RayTraceResult RayTraceResult1 = axisalignedbb1.calculateIntercept(vec3d1, vec3d); + + if (RayTraceResult1 != null) + { + double d1 = vec3d1.distanceTo(RayTraceResult1.hitVec); + + if (d1 < d0 || d0 == 0.0D) + { + entity = entity1; + d0 = d1; + } + } + } + } + + if (entity != null) + { + raytraceresult = new RayTraceResult(entity); + } + + // Players that are considered invulnerable to the caster allow the projectile to pass straight through them. + if (raytraceresult != null && raytraceresult.entityHit != null && raytraceresult.entityHit instanceof EntityPlayer) + { + EntityPlayer entityplayer = (EntityPlayer)raytraceresult.entityHit; + + if (entityplayer.capabilities.disableDamage || this.getShootingEntity() instanceof EntityPlayer && !((EntityPlayer)this.getShootingEntity()).canAttackPlayer(entityplayer)) + { + raytraceresult = null; + } + } + + // If the arrow hits something + if (raytraceresult != null) + { + // If the arrow hits an entity + if (raytraceresult.entityHit != null) + { + DamageSource damagesource = null; + + if (this.getShootingEntity() == null) + { + damagesource = DamageSource.causeThrownDamage(this, this); + } + else + { + damagesource = MagicDamage.causeIndirectMagicDamage(this, (EntityLivingBase)this.getShootingEntity(), this.getDamageType()).setProjectile(); + } + + if (raytraceresult.entityHit.attackEntityFrom(damagesource, (float)(this.getDamage()*this.damageMultiplier))) + { + if (raytraceresult.entityHit instanceof EntityLivingBase) + { + EntityLivingBase entityHit = (EntityLivingBase)raytraceresult.entityHit; + + this.onEntityHit(entityHit); + + if (this.knockbackStrength > 0) + { + float f4 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); + + if (f4 > 0.0F) + { + raytraceresult.entityHit.addVelocity(this.motionX * (double)this.knockbackStrength * 0.6000000238418579D / (double)f4, 0.1D, this.motionZ * (double)this.knockbackStrength * 0.6000000238418579D / (double)f4); + } + } + + // Thorns enchantment + if (this.getShootingEntity() != null && this.getShootingEntity() instanceof EntityLivingBase) + { + EnchantmentHelper.applyThornEnchantments(entityHit, this.getShootingEntity()); + EnchantmentHelper.applyArthropodEnchantments((EntityLivingBase)this.getShootingEntity(), entityHit); + } + + if (this.getShootingEntity() != null && raytraceresult.entityHit != this.getShootingEntity() && raytraceresult.entityHit instanceof EntityPlayer && this.getShootingEntity() instanceof EntityPlayerMP) + { + ((EntityPlayerMP)this.getShootingEntity()).connection.sendPacket(new SPacketChangeGameState(6, 0.0F)); + } + } + + if (!(raytraceresult.entityHit instanceof EntityEnderman) && !this.doOverpenetration()) + { + this.setDead(); + } + } + else + { + if(!this.doOverpenetration()) this.setDead(); + + // Was the 'rebound' that happened when entities were immune to damage + /* + this.motionX *= -0.10000000149011612D; + this.motionY *= -0.10000000149011612D; + this.motionZ *= -0.10000000149011612D; + this.rotationYaw += 180.0F; + this.prevRotationYaw += 180.0F; + this.ticksInAir = 0; + */ + } + } + // If the arrow hits a block + else + { + this.blockX = raytraceresult.getBlockPos().getX(); + this.blockY = raytraceresult.getBlockPos().getY(); + this.blockZ = raytraceresult.getBlockPos().getZ(); + this.stuckInBlock = this.worldObj.getBlockState(raytraceresult.getBlockPos()); + this.motionX = (double)((float)(raytraceresult.hitVec.xCoord - this.posX)); + this.motionY = (double)((float)(raytraceresult.hitVec.yCoord - this.posY)); + this.motionZ = (double)((float)(raytraceresult.hitVec.zCoord - this.posZ)); + //f2 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ); + //this.posX -= this.motionX / (double)f2 * 0.05000000074505806D; + //this.posY -= this.motionY / (double)f2 * 0.05000000074505806D; + //this.posZ -= this.motionZ / (double)f2 * 0.05000000074505806D; + //this.playSound("random.bowhit", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + this.inGround = true; + this.arrowShake = 7; + + this.onBlockHit(); + + if (this.stuckInBlock.getMaterial() != Material.AIR) + { + this.stuckInBlock.getBlock().onEntityCollidedWithBlock(this.worldObj, raytraceresult.getBlockPos(), this.stuckInBlock, this); + } + } + } + + this.posX += this.motionX; + this.posY += this.motionY; + this.posZ += this.motionZ; + //f2 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); + this.rotationYaw = (float)(Math.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI); + + //for (this.rotationPitch = (float)(Math.atan2(this.motionY, (double)f2) * 180.0D / Math.PI); this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F) + //{ + // ; + //} + + while (this.rotationPitch - this.prevRotationPitch >= 180.0F) + { + this.prevRotationPitch += 360.0F; + } + + while (this.rotationYaw - this.prevRotationYaw < -180.0F) + { + this.prevRotationYaw -= 360.0F; + } + + while (this.rotationYaw - this.prevRotationYaw >= 180.0F) + { + this.prevRotationYaw += 360.0F; + } + + this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F; + this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F; + + float f3 = 0.99F; + + if (this.isInWater()) + { + for (int l = 0; l < 4; ++l) + { + float f4 = 0.25F; + this.worldObj.spawnParticle(EnumParticleTypes.WATER_BUBBLE, this.posX - this.motionX * (double)f4, this.posY - this.motionY * (double)f4, this.posZ - this.motionZ * (double)f4, this.motionX, this.motionY, this.motionZ); + } + + f3 = 0.8F; + } + + if (this.isWet()) + { + this.extinguish(); + } + + if(this.doDeceleration()){ + this.motionX *= (double)f3; + this.motionY *= (double)f3; + this.motionZ *= (double)f3; + } + + if(this.doGravity()) this.motionY -= 0.05; + + this.setPosition(this.posX, this.posY, this.posZ); + this.doBlockCollisions(); + } + } + + @Override + public void writeEntityToNBT(NBTTagCompound tag) + { + tag.setShort("xTile", (short)this.blockX); + tag.setShort("yTile", (short)this.blockY); + tag.setShort("zTile", (short)this.blockZ); + tag.setShort("life", (short)this.ticksInGround); + if(this.stuckInBlock != null){ + ResourceLocation resourcelocation = (ResourceLocation)Block.REGISTRY.getNameForObject(this.stuckInBlock.getBlock()); + tag.setString("inTile", resourcelocation == null ? "" : resourcelocation.toString()); + } + tag.setByte("inData", (byte)this.inData); + tag.setByte("shake", (byte)this.arrowShake); + tag.setByte("inGround", (byte)(this.inGround ? 1 : 0)); + tag.setFloat("damageMultiplier", this.damageMultiplier); + if(this.getShootingEntity() != null){ + tag.setUniqueId("casterUUID", this.getShootingEntity().getUniqueID()); + } + } + + @Override + public void readEntityFromNBT(NBTTagCompound tag) + { + this.blockX = tag.getShort("xTile"); + this.blockY = tag.getShort("yTile"); + this.blockZ = tag.getShort("zTile"); + this.ticksInGround = tag.getShort("life"); + // Commented out for now because there's some funny stuff going on with blockstates and metadata. + //this.stuckInBlock = Block.getBlockById(tag.getByte("inTile") & 255); + this.inData = tag.getByte("inData") & 255; + this.arrowShake = tag.getByte("shake") & 255; + this.inGround = tag.getByte("inGround") == 1; + this.damageMultiplier = tag.getFloat("damageMultiplier"); + casterUUID = tag.getUniqueId("casterUUID"); + } + + /** + * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to + * prevent them from trampling crops + */ + protected boolean canTriggerWalking() + { + return false; + } + + @SideOnly(Side.CLIENT) + public float getShadowSize() + { + return 0.0F; + } + + /** + * Sets the amount of knockback the arrow applies when it hits a mob. + */ + public void setKnockbackStrength(int p_70240_1_) + { + this.knockbackStrength = p_70240_1_; + } + + /** + * If returns false, the item will not inflict any damage against entities. + */ + public boolean canAttackWithItem() + { + return false; + } + + public void writeSpawnData(ByteBuf buffer){ + if(this.getShootingEntity() != null) buffer.writeInt(this.getShootingEntity().getEntityId()); + } + + public void readSpawnData(ByteBuf buffer){ + if(buffer.isReadable()) this.shootingEntity = new WeakReference((EntityLivingBase)this.worldObj.getEntityByID(buffer.readInt())); + } + + /** + * Returns the EntityLivingBase that created this construct, or null if it no longer exists. Cases where the + * entity may no longer exist are: entity died or was deleted, mob despawned, player logged out, entity teleported + * to another dimension, or this construct simply had no caster in the first place. + */ + public EntityLivingBase getShootingEntity() { + return shootingEntity == null ? null : shootingEntity.get(); + } + + public void setShootingEntity(EntityLivingBase entity) { + shootingEntity = new WeakReference(entity); + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicMissile.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicMissile.java new file mode 100644 index 00000000..67c079d9 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicMissile.java @@ -0,0 +1,82 @@ +package electroblob.wizardry.entity.projectile; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.SoundEvents; +import net.minecraft.world.World; + +public class EntityMagicMissile extends EntityMagicArrow { + + /** Basic shell constructor. Should only be used by the client. */ + public EntityMagicMissile(World world) { + super(world); + } + + /** Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this + * constructor and then call setVelocity() as that method is, bizarrely, client-side only. */ + public EntityMagicMissile(World world, double x, double y, double z) + { + super(world, x, y, z); + } + + /** Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be altered + * slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on easy, 6 on + * normal and 2 on hard difficulty. */ + public EntityMagicMissile(World world, EntityLivingBase caster, Entity target, float speed, float aimingError, float damageMultiplier) + { + super(world, caster, target, speed, aimingError, damageMultiplier); + } + + /** Creates a projectile pointing in the direction the caster is looking, with the given speed. + * USE THIS CONSTRUCTOR FOR NORMAL SPELLS. */ + public EntityMagicMissile(World world, EntityLivingBase caster, float speed, float damageMultiplier) + { + super(world, caster, speed, damageMultiplier); + } + + @Override + public void onEntityHit(EntityLivingBase entityHit){ + this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + } + + @Override + public void tickInAir(){ + + if (this.ticksExisted > 20){ + this.setDead(); + } + + if(this.worldObj.isRemote){ + + if(this.ticksExisted % 2 == 1){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX, this.posY, this.posZ, 0, 0, 0, 20 + rand.nextInt(10), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2)); + } + else{ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX, this.posY, this.posZ, 0, 0, 0, 20 + rand.nextInt(10), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2)); + } + } + } + + @Override + public double getDamage(){ + return 4.0d; + } + + @Override + public boolean doGravity(){ + return false; + } + + @Override + public boolean doDeceleration(){ + return false; + } + + @Override + protected void entityInit() { + + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicProjectile.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicProjectile.java new file mode 100644 index 00000000..e37c45fa --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicProjectile.java @@ -0,0 +1,79 @@ +package electroblob.wizardry.entity.projectile; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.projectile.EntityThrowable; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; + +/** This class is a generic superclass for all non-directed projectiles, namely: darkness orb, firebolt, firebomb, + * force orb, ice charge, lightning disc, poison bomb, spark, spark bomb and thunderbolt. Directed (arrow-like) projectiles + * should instead extend {@link EntityMagicArrow}. + *

    + * This class purely handles saving of the damage multiplier; EntityThrowable is pretty well suited to my purposes + * as it is. Range is done via the velocity when the constructor is called. Caster is already handled by EntityThrowable.getThrower(). + * + * Note that this class does not implement {@link IEntityAdditionalSpawnData}; subclasses that need to transfer extra data + * to the client should implement that interface themselves. See {@link EntityBomb} for an example. + * @since Wizardry 1.0 + * @author Electroblob + * @see EntityBomb + */ +public abstract class EntityMagicProjectile extends EntityThrowable { + + public float damageMultiplier = 1.0f; + + public EntityMagicProjectile(World world) + { + super(world); + } + + public EntityMagicProjectile(World world, EntityLivingBase thrower) + { + super(world, thrower); + } + + public EntityMagicProjectile(World world, EntityLivingBase thrower, float damageMultiplier) + { + super(world, thrower); + // This is the standard set of parameters for this method, used by snowballs and ender pearls amongst others. + this.setHeadingFromThrower(thrower, thrower.rotationPitch, thrower.rotationYaw, 0.0f, this.getSpeed(), 1.0f); + this.damageMultiplier = damageMultiplier; + } + + public EntityMagicProjectile(World par1World, double par2, double par4, double par6) + { + super(par1World, par2, par4, par6); + } + + /** This got removed at some point since 1.7.10, but I liked it so I thought I'd add it back in again. */ + protected float getSpeed(){ + return 1.5f; + } + + /** Sets this projectile's velocity as a normalised vector towards the target. */ + public void directTowards(Entity target, float velocity){ + + double dx = target.posX - this.posX; + double dy = target.getEntityBoundingBox().minY + (double)(target.height / 2.0F) - (this.posY + (double)(this.height / 2.0F)); + double dz = target.posZ - this.posZ; + + this.motionX = dx/this.getDistanceToEntity(target) * velocity; + this.motionY = dy/this.getDistanceToEntity(target) * velocity; + this.motionZ = dz/this.getDistanceToEntity(target) * velocity; + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + damageMultiplier = nbttagcompound.getFloat("damageMultiplier"); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + nbttagcompound.setFloat("damageMultiplier", damageMultiplier); + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityPoisonBomb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityPoisonBomb.java new file mode 100644 index 00000000..717aa618 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityPoisonBomb.java @@ -0,0 +1,85 @@ +package electroblob.wizardry.entity.projectile; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.MobEffects; +import net.minecraft.init.SoundEvents; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class EntityPoisonBomb extends EntityBomb { + + public EntityPoisonBomb(World par1World) + { + super(par1World); + } + + public EntityPoisonBomb(World par1World, EntityLivingBase par2EntityLivingBase) + { + super(par1World, par2EntityLivingBase); + } + + public EntityPoisonBomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier) + { + super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier); + } + + public EntityPoisonBomb(World par1World, double par2, double par4, double par6) + { + super(par1World, par2, par4, par6); + } + + @Override + protected void onImpact(RayTraceResult par1RayTraceResult) + { + Entity entityHit = par1RayTraceResult.entityHit; + + if (entityHit != null) + { + // This is if the poison bomb gets a direct hit + float damage = 5 * damageMultiplier; + + entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.POISON).setProjectile(), damage); + + if(entityHit instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.POISON, entityHit)) ((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(MobEffects.POISON, 120, 1)); + } + + // Particle effect + if(worldObj.isRemote){ + for(int i=0;i<60*blastMultiplier;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 35, 0.2f + rand.nextFloat()*0.3f, 0.6f, 0.0f); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, 0.2f + rand.nextFloat()*0.2f, 0.8f, 0.0f); + } + // Spawning this after the other particles fixes the rendering colour bug. It's a bit of a cheat, but it works pretty well. + this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0); + } + + if(!this.worldObj.isRemote){ + + this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5F, rand.nextFloat() * 0.4F + 0.6F); + this.playSound(SoundEvents.BLOCK_FIRE_EXTINGUISH, 1.2F, 1.0f); + + double range = 3.0d*blastMultiplier; + + List targets = WizardryUtilities.getEntitiesWithinRadius(range, this.posX, this.posY, this.posZ, this.worldObj); + + for(EntityLivingBase target : targets){ + if(target != entityHit && target != this.getThrower() && !MagicDamage.isEntityImmune(DamageType.POISON, target)){ + target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.POISON), 4.0f * damageMultiplier); + target.addPotionEffect(new PotionEffect(MobEffects.POISON, 100, 1)); + } + } + + this.setDead(); + } + } +} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java new file mode 100644 index 00000000..1ed5dea0 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java @@ -0,0 +1,81 @@ +package electroblob.wizardry.entity.projectile; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.init.SoundEvents; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class EntitySmokeBomb extends EntityBomb { + + public EntitySmokeBomb(World par1World) + { + super(par1World); + } + + public EntitySmokeBomb(World par1World, EntityLivingBase par2EntityLivingBase) + { + super(par1World, par2EntityLivingBase); + } + + public EntitySmokeBomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier) + { + super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier); + } + + public EntitySmokeBomb(World par1World, double par2, double par4, double par6) + { + super(par1World, par2, par4, par6); + } + + @Override + protected void onImpact(RayTraceResult par1RayTraceResult){ + + // Particle effect + if(worldObj.isRemote){ + this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0); + for(int i=0;i<60*blastMultiplier;i++){ + this.worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0, 0, 0); + float brightness = rand.nextFloat() * 0.3f; + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, brightness, brightness, brightness); + } + } + + if(!this.worldObj.isRemote){ + + this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5F, rand.nextFloat() * 0.4F + 0.6F); + this.playSound(SoundEvents.BLOCK_FIRE_EXTINGUISH, 1.2F, 1.0f); + + double range = 3.0d * blastMultiplier; + + List targets = WizardryUtilities.getEntitiesWithinRadius(range, this.posX, this.posY, this.posZ, this.worldObj); + + for(EntityLivingBase target : targets){ + if(target != this.getThrower()){ + // Gives the target blindness if it is a player, mind trick otherwise (since this has the desired + // effect of preventing targeting) + if(target instanceof EntityPlayer){ + target.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, 120, 0)); + }else if(target instanceof EntityLiving){ + // New AI + ((EntityLiving)target).setAttackTarget(null); + + target.addPotionEffect(new PotionEffect(WizardryPotions.mind_trick, 120, 0)); + } + } + } + + this.setDead(); + } + } +} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntitySpark.java b/src/main/java/electroblob/wizardry/entity/projectile/EntitySpark.java new file mode 100644 index 00000000..4061b954 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntitySpark.java @@ -0,0 +1,116 @@ +package electroblob.wizardry.entity.projectile; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class EntitySpark extends EntityMagicProjectile { + + public EntitySpark(World par1World) + { + super(par1World); + } + + public EntitySpark(World par1World, EntityLivingBase par2EntityLivingBase) + { + super(par1World, par2EntityLivingBase); + } + + public EntitySpark(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier) + { + super(par1World, par2EntityLivingBase, damageMultiplier); + } + + public EntitySpark(World par1World, double par2, double par4, double par6) + { + super(par1World, par2, par4, par6); + } + + /** This is the speed */ + protected float getSpeed() + { + return 0.5F; + } + + /** + * Called when this EntityThrowable hits a block or entity. + */ + protected void onImpact(RayTraceResult par1RayTraceResult) + { + Entity entityHit = par1RayTraceResult.entityHit; + + if (entityHit != null){ + + float damage = 6 * damageMultiplier; + entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK), damage); + + } + + this.playSound(WizardrySounds.SPELL_SPARK, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + + // Particle effect + if(worldObj.isRemote){ + for(int i=0;i<8;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); + } + } + + this.setDead(); + } + + public void onUpdate(){ + + super.onUpdate(); + + if(!this.isCollided && !worldObj.isRemote){ + + double seekingRange = 5.0d; + + List entities = WizardryUtilities.getEntitiesWithinRadius(seekingRange, this.posX, this.posY, this.posZ, this.worldObj); + Entity target = null; + + for(Entity possibleTarget : entities){ + // Decides if current entity should be replaced. + if(target == null || this.getDistanceToEntity(target) > this.getDistanceToEntity(possibleTarget)){ + // Decides if new entity is a valid target. + if(WizardryUtilities.isValidTarget(this.getThrower(), possibleTarget)){ + target = possibleTarget; + } + } + } + + if(target != null && Math.abs(this.motionX) < 1 && Math.abs(this.motionY) < 1 && Math.abs(this.motionZ) < 1){ + this.addVelocity((target.posX - this.posX)/30, (target.posY + target.height/2 - this.posY)/30, (target.posZ - this.posZ)/30); + } + } + + if(this.ticksExisted > 100){ + this.setDead(); + } + } + + /** + * Gets the amount of gravity to apply to the thrown entity with each tick. + */ + protected float getGravityVelocity() + { + return 0.0F; + } + + /** + * Return whether this entity should be rendered as on fire. + */ + public boolean canRenderOnFire() + { + return false; + } +} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntitySparkBomb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntitySparkBomb.java new file mode 100644 index 00000000..9ffedcdf --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntitySparkBomb.java @@ -0,0 +1,124 @@ +package electroblob.wizardry.entity.projectile; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.EntityArc; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class EntitySparkBomb extends EntityBomb { + + /** For client use, because thrower field is not visible. */ + private int casterID; + + public EntitySparkBomb(World par1World) + { + super(par1World); + } + + public EntitySparkBomb(World par1World, EntityLivingBase par2EntityLivingBase) + { + super(par1World, par2EntityLivingBase); + } + + public EntitySparkBomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier) + { + super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier); + } + + public EntitySparkBomb(World par1World, double par2, double par4, double par6) + { + super(par1World, par2, par4, par6); + } + + /** + * Called when this EntityThrowable hits a block or entity. + */ + protected void onImpact(RayTraceResult par1RayTraceResult) + { + this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST_FAR, 0.5f, 0.5f); + + Entity entityHit = par1RayTraceResult.entityHit; + + if (entityHit != null) + { + // This is if the spark bomb gets a direct hit + float damage = 6 * damageMultiplier; + + this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + + entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK).setProjectile(), damage); + + } + + // Particle effect + if(worldObj.isRemote){ + for(int i=0;i<8;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); + worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0); + } + } + + double seekerRange = 5.0d * blastMultiplier; + + List targets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, this.posX, this.posY, this.posZ, this.worldObj); + + for(int i=0; i 8){ + this.setDead(); + } + } + + /** + * Gets the amount of gravity to apply to the thrown entity with each tick. + */ + protected float getGravityVelocity() + { + return 0.0F; + } + + /** + * Return whether this entity should be rendered as on fire. + */ + public boolean canRenderOnFire() + { + return false; + } +} diff --git a/src/main/java/electroblob/wizardry/event/DiscoverSpellEvent.java b/src/main/java/electroblob/wizardry/event/DiscoverSpellEvent.java new file mode 100644 index 00000000..48ca70a9 --- /dev/null +++ b/src/main/java/electroblob/wizardry/event/DiscoverSpellEvent.java @@ -0,0 +1,35 @@ +package electroblob.wizardry.event; + +import electroblob.wizardry.spell.Spell; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.fml.common.eventhandler.Cancelable; + +/** + * [NYI] DiscoverSpellEvent is fired when a player discovers a spell by any method.
    + *
    + * This event is {@link Cancelable}.
    + * If this event is canceled, the spell is not discovered.
    + *
    + * This event does not have a result. {@link HasResult}
    + *
    + * This event is fired on the {@link MinecraftForge#EVENT_BUS}. + * + * @author Electroblob + * @since Wizardry 2.1 + */ +public class DiscoverSpellEvent extends PlayerEvent { + + public DiscoverSpellEvent(EntityPlayer player, Spell spell, Source source) { + super(player); + } + + public enum Source { + /** Signifies that the spell was discovered by trying to cast it. */ CASTING, + /** Signifies that the spell was discovered using a scroll of identification. */ IDENTIFICATION_SCROLL, + /** Signifies that the spell was discovered using commands. */ COMMAND, + /** Signifies that the spell was discovered by some other means (. */ OTHER + } + +} diff --git a/src/main/java/electroblob/wizardry/event/SpellBindEvent.java b/src/main/java/electroblob/wizardry/event/SpellBindEvent.java new file mode 100644 index 00000000..f1a88f43 --- /dev/null +++ b/src/main/java/electroblob/wizardry/event/SpellBindEvent.java @@ -0,0 +1,28 @@ +package electroblob.wizardry.event; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.fml.common.eventhandler.Cancelable; + +/** + * [NYI] SpellBindEvent is fired when a player presses the apply button in the arcane workbench.
    + *
    + * This event is {@link Cancelable}.
    + * If this event is canceled, no further processing takes place: spells are not bound, upgrades are not applied and + * crystals are not consumed.
    + *
    + * This event does not have a result. {@link HasResult}
    + *
    + * This event is fired on the {@link MinecraftForge#EVENT_BUS}. + * + * @author Electroblob + * @since Wizardry 2.1 + */ +public class SpellBindEvent extends PlayerEvent { + + public SpellBindEvent(EntityPlayer player) { + super(player); + } + +} diff --git a/src/main/java/electroblob/wizardry/event/SpellCastEvent.java b/src/main/java/electroblob/wizardry/event/SpellCastEvent.java new file mode 100644 index 00000000..2c5346df --- /dev/null +++ b/src/main/java/electroblob/wizardry/event/SpellCastEvent.java @@ -0,0 +1,22 @@ +package electroblob.wizardry.event; + +import electroblob.wizardry.spell.Spell; +import net.minecraft.entity.EntityLivingBase; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.living.LivingEvent; + +/** + * [NYI] SpellCastEvent is the parent event class for all spell casting events.
    + *
    + * This event is fired on the {@link MinecraftForge#EVENT_BUS}. + * + * @author Electroblob + * @since Wizardry 2.1 + */ +public abstract class SpellCastEvent extends LivingEvent { + + public SpellCastEvent(EntityLivingBase entity, Spell spell) { + super(entity); + } + +} diff --git a/src/main/java/electroblob/wizardry/item/IConjuredItem.java b/src/main/java/electroblob/wizardry/item/IConjuredItem.java new file mode 100644 index 00000000..6ae44c6e --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/IConjuredItem.java @@ -0,0 +1,32 @@ +package electroblob.wizardry.item; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +/** Allows wizardry to identify items that are conjured (and therefore need destroying if they leave the inventory) + * without explicitly referencing each, thereby allowing for better expandibility. */ +public interface IConjuredItem { + + /** The NBT tag key used to store the duration multiplier for conjured items. */ + public static final String DURATION_MULTIPLIER_KEY = "durationMultiplier"; + + /** Helper method for setting the duration multiplier (via NBT) for conjured items. */ + public static void setDurationMultiplier(ItemStack stack, float multiplier){ + if(!stack.hasTagCompound()) stack.setTagCompound(new NBTTagCompound()); + stack.getTagCompound().setFloat(DURATION_MULTIPLIER_KEY, multiplier); + } + + /** Helper method for returning the max damage of a conjured item based on its NBT data. Centralises the code. + * Implementors will almost certainly want to call this from {@link Item#getMaxDamage(ItemStack stack)}. */ + public default int getMaxDamageFromNBT(ItemStack stack){ + if(stack.hasTagCompound() && stack.getTagCompound().hasKey(DURATION_MULTIPLIER_KEY)){ + return (int)(this.getBaseDuration() * stack.getTagCompound().getFloat(DURATION_MULTIPLIER_KEY)); + } + return this.getBaseDuration(); + } + + /** Returns the base duration in ticks for this conjured item. Should be a constant (commonly 600). + * Implementors may want to call this when setting an item's max damage in its constructor. */ + public int getBaseDuration(); +} diff --git a/src/main/java/electroblob/wizardry/item/ItemArcaneTome.java b/src/main/java/electroblob/wizardry/item/ItemArcaneTome.java new file mode 100644 index 00000000..dd7688d7 --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemArcaneTome.java @@ -0,0 +1,59 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryTabs; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumRarity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemArcaneTome extends Item { + + public ItemArcaneTome() { + super(); + this.setHasSubtypes(true); + this.setMaxStackSize(1); + this.setCreativeTab(WizardryTabs.WIZARDRY); + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item item, CreativeTabs tab, List list){ + for(int i=1; i tooltip, boolean showAdvanced){ + Tier tier = Tier.values()[stack.getItemDamage()]; + Tier tier2 = Tier.values()[stack.getItemDamage()-1]; + tooltip.add(tier.getDisplayNameWithFormatting()); + tooltip.add("\u00A77" + net.minecraft.client.resources.I18n.format("item.wizardry:arcane_tome.desc1", tier2.getDisplayNameWithFormatting())); + tooltip.add("\u00A77" + net.minecraft.client.resources.I18n.format("item.wizardry:arcane_tome.desc2", tier.getDisplayNameWithFormatting() + "\u00A77")); + } + +} diff --git a/src/main/java/electroblob/wizardry/item/ItemArmourUpgrade.java b/src/main/java/electroblob/wizardry/item/ItemArmourUpgrade.java new file mode 100644 index 00000000..b7b96f10 --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemArmourUpgrade.java @@ -0,0 +1,46 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import electroblob.wizardry.registry.WizardryTabs; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumRarity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemArmourUpgrade extends Item { + + public ItemArmourUpgrade() { + super(); + this.setMaxStackSize(1); + this.setCreativeTab(WizardryTabs.WIZARDRY); + } + + @Override + public EnumRarity getRarity(ItemStack stack){ + return EnumRarity.EPIC; + } + + @Override + @SideOnly(Side.CLIENT) + public boolean hasEffect(ItemStack stack){ + return true; + } + + @Override + @SideOnly(Side.CLIENT) + public void addInformation(ItemStack stack, EntityPlayer player, List tooltip, boolean advanced){ + tooltip.add(net.minecraft.client.resources.I18n.format("item.wizardry:armour_upgrade.desc1", "\u00A77")); + tooltip.add(net.minecraft.client.resources.I18n.format("item.wizardry:armour_upgrade.desc2", "\u00A77", "\u00A7d")); + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item item, CreativeTabs tab, List subItems){ + subItems.add(new ItemStack(this, 1)); + } + +} diff --git a/src/main/java/electroblob/wizardry/item/ItemFirebomb.java b/src/main/java/electroblob/wizardry/item/ItemFirebomb.java new file mode 100644 index 00000000..add289ef --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemFirebomb.java @@ -0,0 +1,50 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import electroblob.wizardry.entity.projectile.EntityFirebomb; +import electroblob.wizardry.registry.WizardryTabs; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemFirebomb extends Item { + + public ItemFirebomb(){ + this.maxStackSize = 16; + this.setCreativeTab(WizardryTabs.WIZARDRY); + } + + @Override + public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ + + if(!player.capabilities.isCreativeMode){ + --stack.stackSize; + } + + player.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); + + if(!world.isRemote){ + EntityFirebomb firebomb = new EntityFirebomb(world, player); + // This is the standard set of parameters for this method, used by snowballs and ender pearls. + firebomb.setHeadingFromThrower(player, player.rotationPitch, player.rotationYaw, 0.0f, 1.5f, 1.0f); + world.spawnEntityInWorld(firebomb); + } + + return ActionResult.newResult(EnumActionResult.SUCCESS, stack); + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item parItem, CreativeTabs parTab, List parListSubItems){ + parListSubItems.add(new ItemStack(this, 1)); + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/item/ItemFlamingAxe.java b/src/main/java/electroblob/wizardry/item/ItemFlamingAxe.java new file mode 100644 index 00000000..28e1400b --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemFlamingAxe.java @@ -0,0 +1,92 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemAxe; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemFlamingAxe extends ItemAxe implements IConjuredItem { + + public ItemFlamingAxe(ToolMaterial material) { + super(material, 8, -3); + this.setMaxDamage(getBaseDuration()); + this.setNoRepair(); + this.setCreativeTab(null); + } + + @Override + public int getBaseDuration(){ + return 600; + } + + @Override + public int getMaxDamage(ItemStack stack){ + return this.getMaxDamageFromNBT(stack); + } + + @Override + // This method allows the code for the item's timer to be greatly simplified by damaging it directly from + // onUpdate() and removing the workaround that involved WizardData and all sorts of crazy stuff. + public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){ + + if(oldStack != null || newStack != null){ + // We only care about the situation where we specifically want the animation NOT to play. + if(oldStack.getItem() == newStack.getItem() && !slotChanged) return false; + } + + return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged); + } + + @Override + public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean selected){ + int damage = stack.getItemDamage(); + if(damage > stack.getMaxDamage()) entity.replaceItemInInventory(slot, null); + stack.setItemDamage(damage + 1); + } + + @Override + public boolean hitEntity(ItemStack stack, EntityLivingBase target, EntityLivingBase wielder){ + if(!MagicDamage.isEntityImmune(DamageType.FIRE, target)) target.setFire(8); + return false; + } + + @Override + @SideOnly(Side.CLIENT) + public boolean hasEffect(ItemStack stack){ + return true; + } + + @Override + public boolean getIsRepairable(ItemStack stack, ItemStack par2ItemStack){ + return false; + } + + @Override + public int getItemEnchantability(){ + return 0; + } + + // Cannot be dropped + @Override + public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player){ + return false; + } + + @Override + @SideOnly(Side.CLIENT) + // Why does this method pass in an item as an argument? It is always the same item the method is invoked on. + public void getSubItems(Item item, CreativeTabs tab, List items){ + items.add(new ItemStack(this, 1)); + } + +} diff --git a/src/main/java/electroblob/wizardry/item/ItemFrostAxe.java b/src/main/java/electroblob/wizardry/item/ItemFrostAxe.java new file mode 100644 index 00000000..0e6100e1 --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemFrostAxe.java @@ -0,0 +1,94 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemAxe; +import net.minecraft.item.ItemStack; +import net.minecraft.potion.PotionEffect; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemFrostAxe extends ItemAxe implements IConjuredItem { + + public ItemFrostAxe(ToolMaterial material) { + super(material, 8, -3); + this.setMaxDamage(getBaseDuration()); + this.setNoRepair(); + this.setCreativeTab(null); + } + + @Override + public int getBaseDuration(){ + return 600; + } + + @Override + public int getMaxDamage(ItemStack stack){ + return this.getMaxDamageFromNBT(stack); + } + + @Override + // This method allows the code for the item's timer to be greatly simplified by damaging it directly from + // onUpdate() and removing the workaround that involved WizardData and all sorts of crazy stuff. + public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){ + + if(oldStack != null || newStack != null){ + // We only care about the situation where we specifically want the animation NOT to play. + if(oldStack.getItem() == newStack.getItem() && !slotChanged) return false; + } + + return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged); + } + + @Override + public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean selected){ + int damage = stack.getItemDamage(); + if(damage > stack.getMaxDamage()) entity.replaceItemInInventory(slot, null); + stack.setItemDamage(damage + 1); + } + + @Override + public boolean hitEntity(ItemStack stack, EntityLivingBase target, EntityLivingBase wielder) + { + if(!MagicDamage.isEntityImmune(DamageType.FROST, target)) target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 160, 1)); + return false; + } + + @Override + @SideOnly(Side.CLIENT) + public boolean hasEffect(ItemStack stack){ + return true; + } + + @Override + public boolean getIsRepairable(ItemStack stack, ItemStack par2ItemStack){ + return false; + } + + @Override + public int getItemEnchantability(){ + return 0; + } + + // Cannot be dropped + @Override + public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player){ + return false; + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item parItem, CreativeTabs parTab, List parListSubItems){ + parListSubItems.add(new ItemStack(this, 1)); + } + +} diff --git a/src/main/java/electroblob/wizardry/item/ItemIdentificationScroll.java b/src/main/java/electroblob/wizardry/item/ItemIdentificationScroll.java new file mode 100644 index 00000000..da85487d --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemIdentificationScroll.java @@ -0,0 +1,81 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.registry.WizardryAchievements; +import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemIdentificationScroll extends Item { + + public ItemIdentificationScroll() { + super(); + this.setCreativeTab(WizardryTabs.WIZARDRY); + } + + @Override + @SideOnly(Side.CLIENT) + public boolean hasEffect(ItemStack stack){ + return true; + } + + @Override + @SideOnly(Side.CLIENT) + public void addInformation(ItemStack stack, EntityPlayer player, List tooltip, boolean par4){ + tooltip.add(net.minecraft.client.resources.I18n.format("item.wizardry:identification_scroll.desc1", "\u00A77")); + tooltip.add(net.minecraft.client.resources.I18n.format("item.wizardry:identification_scroll.desc2", "\u00A77")); + } + + @Override + public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ + + if(WizardData.get(player) != null){ + + WizardData properties = WizardData.get(player); + + for(ItemStack stack1 : WizardryUtilities.getPrioritisedHotbarAndOffhand(player)){ + + if(stack1 != null){ + Spell spell = Spell.get(stack1.getItemDamage()); + if((stack1.getItem() instanceof ItemSpellBook || stack1.getItem() instanceof ItemScroll) + && !properties.hasSpellBeenDiscovered(spell)){ + + // Identification scrolls give the chat readout in creative mode, otherwise it looks like + // nothing happens! + properties.discoverSpell(spell); + player.addStat(WizardryAchievements.identify_spell); + player.playSound(SoundEvents.ENTITY_PLAYER_LEVELUP, 1.25f, 1); + if(!player.capabilities.isCreativeMode) stack.stackSize--; + if(!world.isRemote) player.addChatMessage(new TextComponentTranslation("spell.discover", spell.getNameForTranslationFormatted())); + + return new ActionResult(EnumActionResult.SUCCESS, stack); + } + } + } + // If it found nothing to identify, it says so! + if(!world.isRemote) player.addChatMessage(new TextComponentTranslation("item.wizardry:identification_scroll.nothing_to_identify")); + } + + return new ActionResult(EnumActionResult.FAIL, stack); + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item parItem, CreativeTabs parTab, List parListSubItems){ + parListSubItems.add(new ItemStack(this, 1)); + } +} diff --git a/src/main/java/electroblob/wizardry/item/ItemPoisonBomb.java b/src/main/java/electroblob/wizardry/item/ItemPoisonBomb.java new file mode 100644 index 00000000..94261841 --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemPoisonBomb.java @@ -0,0 +1,51 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import electroblob.wizardry.entity.projectile.EntityPoisonBomb; +import electroblob.wizardry.registry.WizardryTabs; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemPoisonBomb extends Item { + + public ItemPoisonBomb(){ + this.maxStackSize = 16; + this.setCreativeTab(WizardryTabs.WIZARDRY); + } + + @Override + public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ + + if(!player.capabilities.isCreativeMode){ + --stack.stackSize; + } + + player.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); + + if(!world.isRemote){ + EntityPoisonBomb poisonbomb = new EntityPoisonBomb(world, player); + // This is the standard set of parameters for this method, used by snowballs and ender pearls. + poisonbomb.setHeadingFromThrower(player, player.rotationPitch, player.rotationYaw, 0.0f, 1.5f, 1.0f); + world.spawnEntityInWorld(poisonbomb); + } + + return ActionResult.newResult(EnumActionResult.SUCCESS, stack); + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item item, CreativeTabs tab, List subItems){ + subItems.add(new ItemStack(this, 1)); + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/item/ItemScroll.java b/src/main/java/electroblob/wizardry/item/ItemScroll.java new file mode 100644 index 00000000..e238b5b8 --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemScroll.java @@ -0,0 +1,139 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.packet.PacketCastSpell; +import electroblob.wizardry.packet.WizardryPacketHandler; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemScroll extends Item { + + public ItemScroll(){ + super(); + this.setHasSubtypes(true); + this.setMaxStackSize(1); + this.setCreativeTab(WizardryTabs.SPELLS); + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item item, CreativeTabs par2CreativeTabs, List list){ + // Isn't this sooooo much neater with the filter thing? + for(Spell spell : Spell.getSpells(Spell.nonContinuousSpells)){ + list.add(new ItemStack(item, 1, spell.id())); + } + } + + @Override + @SideOnly(Side.CLIENT) + public boolean hasEffect(ItemStack stack){ + return true; + } + + @Override + // Item's version of this method is, quite frankly, an abomination. Why is a deprecated method being used as such + // an integral part of the code? And what's the point in getUnlocalisedNameInefficiently? + public String getItemStackDisplayName(ItemStack stack){ + + /* Ok, so this method can be called from either the client or the server side. Obviously, on the client the + * spell name is either translated or obfuscated, then it is put into the item name as part of that translation. + * On the server side, however, there's a problem: on the one hand, the spell name shouldn't be obfuscated in + * case the server wants to do something with it, and in that case returning world-specific gobbledegook is + * not particularly helpful. On the other hand, something might happen that causes this method to be called on + * the server side, but the result to then be sent to the client, which means broken discovery system. + * Simply put, I can't predict that, and it's not my job to cater for other people's incorrect usage of code, + * especially when that might compromise some perfectly reasonable use (think Bibliocraft's 'best guess' book + * detection). */ + // TODO: Backport this proxy-based fix. + return Wizardry.proxy.getScrollDisplayName(stack); + + } + + @Override + public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ + + if(player.isPotionActive(WizardryPotions.arcane_jammer)) return new ActionResult(EnumActionResult.FAIL, stack);; + + Spell spell = Spell.get(stack.getItemDamage()); + + // If a spell is disabled in the config, it will not work. + if(!spell.isEnabled()){ + if(!world.isRemote) player.addChatMessage(new TextComponentTranslation("spell.disabled", spell.getNameForTranslationFormatted())); + return new ActionResult(EnumActionResult.FAIL, stack); + } + + if(!spell.isContinuous){ + + /* + if(spell.chargeup > 0 && !entityplayer.isUsingItem()){ + // Spells with a chargeup time are now handled separately. + entityplayer.setItemInUse(stack, this.getMaxItemUseDuration(stack)); + return stack; + } + */ + + if(!world.isRemote){ + + if(spell.cast(world, player, hand, 0, new SpellModifiers())){ + + if(spell.doesSpellRequirePacket()){ + // Sends a packet to all players in dimension to tell them to spawn particles. + IMessage msg = new PacketCastSpell.Message(player.getEntityId(), hand, spell.id(), new SpellModifiers()); + WizardryPacketHandler.net.sendToDimension(msg, world.provider.getDimension()); + } + + if(!player.capabilities.isCreativeMode && !WizardData.get(player).hasSpellBeenDiscovered(spell) && Wizardry.settings.discoveryMode){ + player.playSound(SoundEvents.ENTITY_PLAYER_LEVELUP, 1.25f, 1); + if(!player.worldObj.isRemote) player.addChatMessage(new TextComponentTranslation("spell.discover", spell.getNameForTranslationFormatted())); + } + WizardData.get(player).discoverSpell(spell); + + if(!player.capabilities.isCreativeMode) stack.stackSize--; + + return new ActionResult(EnumActionResult.SUCCESS, stack); + } + + // Client-inconsistent spell casting. The code inside the else if statement only runs client-side. + // This else if check was bugging me for AGES! I can't believe I didn't compare to ItemWand before. + }else if(!spell.doesSpellRequirePacket()){ + // This is all that needs to happen, because everything above works fine on just the server side. + if(spell.cast(world, player, hand, 0, new SpellModifiers())){ + // Added in version 1.1.3 to fix the client-side spell discovery not updating for spells with the + // packet optimisation. + if(WizardData.get(player) != null){ + WizardData.get(player).discoverSpell(spell); + } + + new ActionResult(EnumActionResult.SUCCESS, stack); + } + } + } + + return new ActionResult(EnumActionResult.FAIL, stack); + + } + + @Override + @SideOnly(Side.CLIENT) + public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack){ + return Wizardry.proxy.getFontRenderer(stack); + } +} diff --git a/src/main/java/electroblob/wizardry/item/ItemSmokeBomb.java b/src/main/java/electroblob/wizardry/item/ItemSmokeBomb.java new file mode 100644 index 00000000..a2180d97 --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemSmokeBomb.java @@ -0,0 +1,50 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import electroblob.wizardry.entity.projectile.EntitySmokeBomb; +import electroblob.wizardry.registry.WizardryTabs; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemSmokeBomb extends Item { + + public ItemSmokeBomb(){ + this.maxStackSize = 16; + this.setCreativeTab(WizardryTabs.WIZARDRY); + } + + @Override + public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ + + if(!player.capabilities.isCreativeMode){ + --stack.stackSize; + } + + player.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); + + if(!world.isRemote){ + EntitySmokeBomb smokebomb = new EntitySmokeBomb(world, player); + // This is the standard set of parameters for this method, used by snowballs and ender pearls. + smokebomb.setHeadingFromThrower(player, player.rotationPitch, player.rotationYaw, 0.0f, 1.5f, 1.0f); + world.spawnEntityInWorld(smokebomb); + } + + return ActionResult.newResult(EnumActionResult.SUCCESS, stack); + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item item, CreativeTabs tab, List subItems){ + subItems.add(new ItemStack(this, 1)); + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/item/ItemSpectralArmour.java b/src/main/java/electroblob/wizardry/item/ItemSpectralArmour.java new file mode 100644 index 00000000..951b69d9 --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemSpectralArmour.java @@ -0,0 +1,110 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.EnumRarity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemArmor; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemSpectralArmour extends ItemArmor implements IConjuredItem { + + public ItemSpectralArmour(ArmorMaterial material, int renderIndex, EntityEquipmentSlot armourType) { + + super(material, renderIndex, armourType); + + this.setCreativeTab(null); + this.setMaxDamage(getBaseDuration()); + } + + @Override + public int getBaseDuration(){ + return 1200; + } + + @Override + public int getMaxDamage(ItemStack stack){ + return this.getMaxDamageFromNBT(stack); + } + + // Overridden to stop the enchantment trick making the name turn blue. + @Override + public EnumRarity getRarity(ItemStack stack){ + return EnumRarity.COMMON; + } + + @Override + // This method allows the code for the item's timer to be greatly simplified by damaging it directly from + // onUpdate() and removing the workaround that involved WizardData and all sorts of crazy stuff. + public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){ + + if(oldStack != null || newStack != null){ + // We only care about the situation where we specifically want the animation NOT to play. + if(oldStack.getItem() == newStack.getItem() && !slotChanged) return false; + } + + return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged); + } + + @Override + public void onArmorTick(World world, EntityPlayer player, ItemStack stack){ + int damage = stack.getItemDamage(); + if(damage > stack.getMaxDamage()) player.inventory.clearMatchingItems(this, -1, 1, null); + stack.setItemDamage(damage + 1); + } + + @Override + @SideOnly(Side.CLIENT) + public boolean hasEffect(ItemStack stack){ + return true; + } + + @Override + public boolean getIsRepairable(ItemStack stack, ItemStack par2ItemStack){ + return false; + } + + @Override + public int getItemEnchantability(){ + return 0; + } + + // Cannot be dropped + @Override + public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player){ + return false; + } + + @Override + public String getArmorTexture(ItemStack stack, Entity entity, EntityEquipmentSlot slot, String type) { + + if(slot == EntityEquipmentSlot.LEGS) return "wizardry:textures/armour/spectral_armour_legs.png"; + + return "wizardry:textures/armour/spectral_armour.png"; + } + + @Override + @SideOnly(Side.CLIENT) + public net.minecraft.client.model.ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack, EntityEquipmentSlot armorSlot, + net.minecraft.client.model.ModelBiped _default) { + net.minecraft.client.renderer.GlStateManager.enableBlend(); + net.minecraft.client.renderer.GlStateManager.tryBlendFuncSeparate(net.minecraft.client.renderer.GlStateManager.SourceFactor.SRC_ALPHA, net.minecraft.client.renderer.GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, + net.minecraft.client.renderer.GlStateManager.SourceFactor.ONE, net.minecraft.client.renderer.GlStateManager.DestFactor.ZERO); + return super.getArmorModel(entityLiving, itemStack, armorSlot, _default); + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item parItem, CreativeTabs parTab, List parListSubItems){ + parListSubItems.add(new ItemStack(this, 1)); + } + +} diff --git a/src/main/java/electroblob/wizardry/item/ItemSpectralBow.java b/src/main/java/electroblob/wizardry/item/ItemSpectralBow.java new file mode 100644 index 00000000..b25efc8d --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemSpectralBow.java @@ -0,0 +1,229 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import javax.annotation.Nullable; + +import electroblob.wizardry.Wizardry; +import net.minecraft.client.Minecraft; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.projectile.EntityArrow; +import net.minecraft.init.Enchantments; +import net.minecraft.init.Items; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.IItemPropertyGetter; +import net.minecraft.item.Item; +import net.minecraft.item.ItemArrow; +import net.minecraft.item.ItemBow; +import net.minecraft.item.ItemStack; +import net.minecraft.stats.StatList; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundCategory; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemSpectralBow extends ItemBow implements IConjuredItem { + + public ItemSpectralBow(){ + super(); + this.setMaxDamage(getBaseDuration()); + this.setNoRepair(); + this.setCreativeTab(null); + this.addPropertyOverride(new ResourceLocation("pull"), new IItemPropertyGetter() + { + @SideOnly(Side.CLIENT) + public float apply(ItemStack stack, @Nullable World worldIn, @Nullable EntityLivingBase entityIn) + { + if (entityIn == null) + { + return 0.0F; + } + else + { + ItemStack itemstack = entityIn.getActiveItemStack(); + // Mojang, observe - hardcoding item references into their own classes is NOT good Java. + return itemstack != null && itemstack.getItem() == ItemSpectralBow.this ? (float)(stack.getMaxItemUseDuration() - entityIn.getItemInUseCount()) / 20.0F : 0.0F; + } + } + }); + this.addPropertyOverride(new ResourceLocation("pulling"), new IItemPropertyGetter() + { + @SideOnly(Side.CLIENT) + public float apply(ItemStack stack, @Nullable World worldIn, @Nullable EntityLivingBase entityIn) + { + return entityIn != null && entityIn.isHandActive() && entityIn.getActiveItemStack() == stack ? 1.0F : 0.0F; + } + }); + } + + @Override + @SideOnly(Side.CLIENT) + // Why does this still exist? Item models deal with this now, right? + public boolean isFull3D(){ + return true; + } + + @Override + public int getBaseDuration(){ + return 600; + } + + @Override + public int getMaxDamage(ItemStack stack){ + return this.getMaxDamageFromNBT(stack); + } + + @Override + // This method allows the code for the item's timer to be greatly simplified by damaging it directly from + // onUpdate() and removing the workaround that involved WizardData and all sorts of crazy stuff. + public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){ + + if(oldStack != null && newStack != null){ + // We only care about the situation where we specifically want the animation NOT to play. + if(oldStack.getItem() == newStack.getItem() && !slotChanged + // This code should only run on the client side, so using Minecraft is ok. + && !Minecraft.getMinecraft().thePlayer.isHandActive()) return false; + } + + return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged); + } + + @Override + public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean selected){ + int damage = stack.getItemDamage(); + if(damage > stack.getMaxDamage()) entity.replaceItemInInventory(slot, null); + // Can't damage it whilst in use because for some reason it causes the item use to constantly reset. + if(!(entity instanceof EntityLivingBase) || !((EntityLivingBase)entity).isHandActive()){ + stack.setItemDamage(damage + 1); + } + } + + // The following two methods re-route the displayed durability through the proxies in order to override the pausing + // of the item timer when the bow is being pulled. + + @Override + public double getDurabilityForDisplay(ItemStack stack){ + return Wizardry.proxy.getConjuredBowDurability(stack); + } + + public double getDefaultDurabilityForDisplay(ItemStack stack){ + return super.getDurabilityForDisplay(stack); + } + + @Override + public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ + + ActionResult ret = net.minecraftforge.event.ForgeEventFactory.onArrowNock(stack, world, player, hand, true); + + if(ret != null) return ret; + + player.setActiveHand(hand); + + return ActionResult.newResult(EnumActionResult.SUCCESS, stack); + + } + + @Override + @SideOnly(Side.CLIENT) + public boolean hasEffect(ItemStack stack){ + return true; + } + + @Override + public boolean getIsRepairable(ItemStack stack, ItemStack stack2){ + return false; + } + + @Override + public int getItemEnchantability(){ + return 0; + } + + // Cannot be dropped + @Override + public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player){ + return false; + } + + @Override + public void onUsingTick(ItemStack stack, EntityLivingBase player, int count){ + // player.getItemInUseMaxCount() is named incorrectly; you only have to look at the method to see what it really + // does. + if(stack.getItemDamage() + player.getItemInUseMaxCount() > stack.getMaxDamage()) + player.replaceItemInInventory(player.getActiveHand() == EnumHand.MAIN_HAND ? 98 : 99, null); + } + + @Override + public void onPlayerStoppedUsing(ItemStack stack, World world, EntityLivingBase entity, int timeLeft){ + // Decreases the timer by the amount it should have been decreased while the bow was in use. + if(!world.isRemote) stack.setItemDamage(stack.getItemDamage() + (this.getMaxItemUseDuration(stack) - timeLeft)); + + if (entity instanceof EntityPlayer){ + + EntityPlayer entityplayer = (EntityPlayer)entity; + + int i = this.getMaxItemUseDuration(stack) - timeLeft; + i = net.minecraftforge.event.ForgeEventFactory.onArrowLoose(stack, world, (EntityPlayer)entity, i, true); + if (i < 0) return; + + float f = getArrowVelocity(i); + + if ((double)f >= 0.1D){ + + if (!world.isRemote){ + + ItemArrow itemarrow = (ItemArrow)Items.ARROW; + EntityArrow entityarrow = itemarrow.createArrow(world, new ItemStack(itemarrow), entityplayer); + entityarrow.setAim(entityplayer, entityplayer.rotationPitch, entityplayer.rotationYaw, 0.0F, f * 3.0F, 1.0F); + + if (f == 1.0F) + { + entityarrow.setIsCritical(true); + } + + int j = EnchantmentHelper.getEnchantmentLevel(Enchantments.POWER, stack); + + if (j > 0) + { + entityarrow.setDamage(entityarrow.getDamage() + (double)j * 0.5D + 0.5D); + } + + int k = EnchantmentHelper.getEnchantmentLevel(Enchantments.PUNCH, stack); + + if (k > 0) + { + entityarrow.setKnockbackStrength(k); + } + + if (EnchantmentHelper.getEnchantmentLevel(Enchantments.FLAME, stack) > 0) + { + entityarrow.setFire(100); + } + + entityarrow.pickupStatus = EntityArrow.PickupStatus.DISALLOWED; + + world.spawnEntityInWorld(entityarrow); + } + + world.playSound((EntityPlayer)null, entityplayer.posX, entityplayer.posY, entityplayer.posZ, SoundEvents.ENTITY_ARROW_SHOOT, SoundCategory.NEUTRAL, 1.0F, 1.0F / (itemRand.nextFloat() * 0.4F + 1.2F) + f * 0.5F); + + entityplayer.addStat(StatList.getObjectUseStats(this)); + } + } + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item item, CreativeTabs tab, List subItems){ + subItems.add(new ItemStack(this, 1)); + } + +} diff --git a/src/main/java/electroblob/wizardry/item/ItemSpectralPickaxe.java b/src/main/java/electroblob/wizardry/item/ItemSpectralPickaxe.java new file mode 100644 index 00000000..a91c9000 --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemSpectralPickaxe.java @@ -0,0 +1,82 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemPickaxe; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemSpectralPickaxe extends ItemPickaxe implements IConjuredItem { + + public ItemSpectralPickaxe(ToolMaterial material){ + super(material); + this.setMaxDamage(getBaseDuration()); + this.setNoRepair(); + this.setCreativeTab(null); + } + + @Override + public int getBaseDuration(){ + return 600; + } + + @Override + public int getMaxDamage(ItemStack stack){ + return this.getMaxDamageFromNBT(stack); + } + + @Override + // This method allows the code for the item's timer to be greatly simplified by damaging it directly from + // onUpdate() and removing the workaround that involved WizardData and all sorts of crazy stuff. + public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){ + + if(oldStack != null || newStack != null){ + // We only care about the situation where we specifically want the animation NOT to play. + if(oldStack.getItem() == newStack.getItem() && !slotChanged) return false; + } + + return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged); + } + + @Override + public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean selected){ + int damage = stack.getItemDamage(); + if(damage > stack.getMaxDamage()) entity.replaceItemInInventory(slot, null); + stack.setItemDamage(damage + 1); + } + + @Override + @SideOnly(Side.CLIENT) + public boolean hasEffect(ItemStack stack){ + return true; + } + + @Override + public boolean getIsRepairable(ItemStack stack, ItemStack par2ItemStack){ + return false; + } + + @Override + public int getItemEnchantability(){ + return 0; + } + + // Cannot be dropped + @Override + public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player){ + return false; + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item item, CreativeTabs tab, List subItems){ + subItems.add(new ItemStack(this, 1)); + } + +} diff --git a/src/main/java/electroblob/wizardry/item/ItemSpectralSword.java b/src/main/java/electroblob/wizardry/item/ItemSpectralSword.java new file mode 100644 index 00000000..b1db6b6e --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemSpectralSword.java @@ -0,0 +1,82 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemSword; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemSpectralSword extends ItemSword implements IConjuredItem { + + public ItemSpectralSword(ToolMaterial material) { + super(material); + this.setMaxDamage(getBaseDuration()); + this.setNoRepair(); + this.setCreativeTab(null); + } + + @Override + public int getBaseDuration(){ + return 600; + } + + @Override + public int getMaxDamage(ItemStack stack){ + return this.getMaxDamageFromNBT(stack); + } + + @Override + // This method allows the code for the item's timer to be greatly simplified by damaging it directly from + // onUpdate() and removing the workaround that involved WizardData and all sorts of crazy stuff. + public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){ + + if(oldStack != null || newStack != null){ + // We only care about the situation where we specifically want the animation NOT to play. + if(oldStack.getItem() == newStack.getItem() && !slotChanged) return false; + } + + return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged); + } + + @Override + public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean selected){ + int damage = stack.getItemDamage(); + if(damage > stack.getMaxDamage()) entity.replaceItemInInventory(slot, null); + stack.setItemDamage(damage + 1); + } + + @Override + @SideOnly(Side.CLIENT) + public boolean hasEffect(ItemStack stack){ + return true; + } + + @Override + public boolean getIsRepairable(ItemStack stack, ItemStack par2ItemStack){ + return false; + } + + @Override + public int getItemEnchantability(){ + return 0; + } + + // Cannot be dropped + @Override + public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player){ + return false; + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item item, CreativeTabs tab, List subItems){ + subItems.add(new ItemStack(this, 1)); + } + +} diff --git a/src/main/java/electroblob/wizardry/item/ItemSpellBook.java b/src/main/java/electroblob/wizardry/item/ItemSpellBook.java new file mode 100644 index 00000000..742c660a --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemSpellBook.java @@ -0,0 +1,82 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import electroblob.wizardry.SpellGlyphData; +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryGuiHandler; +import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.spell.Spell; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.oredict.OreDictionary; + +public class ItemSpellBook extends Item { + + public ItemSpellBook() { + super(); + this.setHasSubtypes(true); + this.setMaxStackSize(1); + this.setCreativeTab(WizardryTabs.SPELLS); + } + + @Override + public void getSubItems(Item item, CreativeTabs tab, List list){ + // In this particular case, getTotalSpellCount() is a more efficient way of doing this since the spell instance + // is not required, only the id. + for(int i = 0; i < Spell.getTotalSpellCount(); i++){ + // i+1 is used so that the metadata ties up with the id() method. In other words, the none spell has id + // 0 and since this is not used as a spell book the metadata starts at 1. + list.add(new ItemStack(item, 1, i+1)); + } + } + + @Override + public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ + player.openGui(Wizardry.instance, WizardryGuiHandler.SPELL_BOOK, world, 0, 0, 0); + return ActionResult.newResult(EnumActionResult.SUCCESS, stack); + } + + @Override + @SideOnly(Side.CLIENT) + public void addInformation(ItemStack itemstack, EntityPlayer player, List tooltip, boolean advanced){ + // Tooltip is left blank for wizards buying generic spell books. + if(itemstack.getItemDamage() != OreDictionary.WILDCARD_VALUE){ + + Spell spell = Spell.get(itemstack.getItemDamage()); + + boolean discovered = true; + if(Wizardry.settings.discoveryMode && !player.capabilities.isCreativeMode && WizardData.get(player) != null + && !WizardData.get(player).hasSpellBeenDiscovered(spell)){ + discovered = false; + } + + // Element colour is not given for undiscovered spells + tooltip.add(discovered ? "\u00A77" + spell.getDisplayNameWithFormatting() : "#\u00A79" + SpellGlyphData.getGlyphName(spell, player.worldObj)); + tooltip.add(spell.tier.getDisplayNameWithFormatting()); + } + /* Removed to streamline the tooltip a bit. Information is now within the book. + if(spell.isContinuous){ + tooltip.add("\u00A79Mana Cost: " + spell.cost + " per second"); + }else{ + tooltip.add("\u00A79Mana Cost: " + spell.cost); + } + */ + } + + @Override + @SideOnly(Side.CLIENT) + public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack){ + return Wizardry.proxy.getFontRenderer(stack); + } + +} diff --git a/src/main/java/electroblob/wizardry/item/ItemWand.java b/src/main/java/electroblob/wizardry/item/ItemWand.java new file mode 100644 index 00000000..59d126e5 --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemWand.java @@ -0,0 +1,430 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import electroblob.wizardry.SpellGlyphData; +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.ISummonedCreature; +import electroblob.wizardry.packet.PacketCastSpell; +import electroblob.wizardry.packet.WizardryPacketHandler; +import electroblob.wizardry.registry.WizardryAchievements; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WandHelper; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.EnumAction; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** This class is (literally) where the magic happens! All wand types are single instances of this class. There's a lot + * of quite hard-to-read code in here, but unfortunately there's not much I can do about that. For this reason, I have + * written the {@link WandHelper} class. I strongly recommend you use it for interacting with wand items wherever + * possible. + *

    + * It's unikely that anything in this class will be of much use externally, but should you wish to use it for whatever + * reason (perhaps if you extend it), it works as follows: + *

    + * - onItemRightClick is where non-continuous spells are cast, and it sets the item in use for continuous spells
    + * - onUsingTick does the casting for continuous spells
    + * - onUpdate deals with the cooldowns for the spells + * @since Wizardry 1.0 */ +public class ItemWand extends Item { + + public Tier tier; + public Element element; + + public ItemWand(Tier tier, Element element) { + super(); + setMaxStackSize(1); + if(element == null || tier == Tier.BASIC){ + setCreativeTab(WizardryTabs.WIZARDRY); + } + this.tier = tier; + this.element = element; + this.setMaxDamage(this.tier.maxCharge); + } + + @Override + @SideOnly(Side.CLIENT) + public boolean isFull3D(){ + return true; + } + + @Override + @SideOnly(Side.CLIENT) + public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack){ + return Wizardry.proxy.getFontRenderer(stack); + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item parItem, CreativeTabs parTab, List parListSubItems){ + parListSubItems.add(new ItemStack(this, 1)); + } + + // Max damage is modifiable with upgrades. + @Override + public int getMaxDamage(ItemStack itemstack){ + // + 0.5f corrects small float errors rounding down + return (int)(super.getMaxDamage(itemstack)*(1.0f + Constants.STORAGE_INCREASE_PER_LEVEL * WandHelper.getUpgradeLevel(itemstack, WizardryItems.storage_upgrade)) + 0.5f); + } + + @Override + public void onCreated(ItemStack stack, World par2World, EntityPlayer par3EntityPlayer){ + /* Removed because of mana flasks, which would cause a new book to be given each time a mana flask + * is crafted with the wand. Handbook is now given on acquiring a crystal. + ExtendedPlayer properties = ExtendedPlayer.get(par3EntityPlayer); + if(properties != null && !properties.handbookGiven){ + par3EntityPlayer.inventory.addItemStackToInventory(new ItemStack(Wizardry.wizardHandbook)); + properties.handbookGiven = true; + }*/ + par3EntityPlayer.addStat(WizardryAchievements.arcane_initiate); + } + + @Override + public void onUpdate(ItemStack itemstack, World world, Entity entity, int slot, boolean isHeld){ + + WandHelper.decrementCooldowns(itemstack); + + // Decrements wand damage (increases mana) every 1.5 seconds if it has a condenser upgrade + if(!world.isRemote && itemstack.isItemDamaged() && world.getWorldTime() % Constants.CONDENSER_TICK_INTERVAL == 0){ + // If the upgrade level is 0, this does nothing anyway. + itemstack.setItemDamage(itemstack.getItemDamage() - WandHelper.getUpgradeLevel(itemstack, WizardryItems.condenser_upgrade)); + } + + if(entity instanceof EntityPlayer && this.element != null && this.element != Element.MAGIC){ + // As it stands, this will trigger every tick. Not ideal, but I can't find a way to detect if a player + // has a certain achievement. + // EDIT: There is a way to check, using StatFileWriter#hasAchievementUnlocked, but this ends up calling the same + // thing as addStat anyway, meaning there's no point and it's probably not much of a problem anyway. + ((EntityPlayer)entity).addStat(WizardryAchievements.elemental); + } + } + + @Override + public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){ + + // This method does some VERY strange things! Despite its name, it also seems to affect the updating of NBT... + + if(oldStack != null || newStack != null){ + // We only care about the situation where we specifically want the animation NOT to play. + if(oldStack.getItem() == newStack.getItem() && !slotChanged + && oldStack.getItem() instanceof ItemWand && newStack.getItem() instanceof ItemWand + && WandHelper.getCurrentSpell(oldStack) == WandHelper.getCurrentSpell(newStack)) return false; + } + + return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged); + } + + @Override + public EnumAction getItemUseAction(ItemStack itemstack){ + return WandHelper.getCurrentSpell(itemstack).action; + } + + @Override + public int getMaxItemUseDuration(ItemStack stack){ + return 72000; + } + + @SideOnly(Side.CLIENT) + @Override + public void addInformation(ItemStack itemstack, EntityPlayer player, List text, boolean advanced){ + + // +0.5f is necessary due to the error in the way floats are calculated. + if(element != null) text.add("\u00A78" + net.minecraft.client.resources.I18n.format("item.wizardry:wand.buff", (int)((tier.level+1)*Constants.DAMAGE_INCREASE_PER_TIER*100 + 0.5f) + "%", element.getDisplayName())); + + Spell spell = WandHelper.getCurrentSpell(itemstack); + + boolean discovered = true; + if(Wizardry.settings.discoveryMode && !player.capabilities.isCreativeMode && WizardData.get(player) != null + && !WizardData.get(player).hasSpellBeenDiscovered(spell)){ + discovered = false; + } + + text.add("\u00A77" + net.minecraft.client.resources.I18n.format("item.wizardry:wand.spell", discovered ? "\u00A77" + spell.getDisplayNameWithFormatting() : "#\u00A79" + SpellGlyphData.getGlyphName(spell, player.worldObj))); + + text.add("\u00A79" + net.minecraft.client.resources.I18n.format("item.wizardry:wand.mana", (this.getMaxDamage(itemstack) - this.getDamage(itemstack)), this.getMaxDamage(itemstack))); + } + + @Override + public String getItemStackDisplayName(ItemStack stack){ + return (this.element == null ? "" : this.element.getFormattingCode()) + super.getItemStackDisplayName(stack); + } + + // Continuous spells use the onUsingItemTick method instead of this one. + /* An important thing to note about this method: it is only called on the server and the client of the player + * holding the item. This means if you spawn particles here they will not show up on other players' screens. + * Instead, this must be done via packets. */ + @Override + public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ + + if(this.selectMinionTarget(player, world)) return new ActionResult(EnumActionResult.SUCCESS, stack); + + if(player.isPotionActive(WizardryPotions.arcane_jammer)) return new ActionResult(EnumActionResult.FAIL, stack); + + Spell spell = WandHelper.getCurrentSpell(stack); + + // If a spell is disabled in the config, it will not work. + if(!spell.isEnabled()){ + if(!world.isRemote) player.addChatMessage(new TextComponentTranslation("spell.disabled", spell.getNameForTranslationFormatted())); + return new ActionResult(EnumActionResult.FAIL, stack); + } + + // This is here to start the inUse thing, otherwise the onItemUsingTick method will not fire. + // If the castSpell method then returns false nothing will happen (continuous spells have no EnumAction + // either so setting the item in use has no direct visible effect). + // Edit: Strictly speaking this is not true but the spells that do have an action (shield, shadow ward and levitation) + // will never return false. + if(spell.isContinuous && !player.isHandActive()){ + player.setActiveHand(hand); + } + + // Conditions for the spell to be attempted. The tier check is a failsafe; it should never be false unless the + // NBT is modified directly. + if(!spell.isContinuous && spell.tier.level <= this.tier.level + // Checks that the wand has enough mana to cast the spell + && spell.cost <= (stack.getMaxDamage() - stack.getItemDamage()) + // Checks that the spell is not in cooldown or that the player is in creative mode + && (WandHelper.getCurrentCooldown(stack) == 0 || player.capabilities.isCreativeMode)){ + + // = Spell modifiers = + SpellModifiers modifiers = this.calculateModifiers(stack, spell); + + // If the spell does not require a packet, the code is run in the old client-inconsistent way, since this + // means that swingItem() doesn't need packets in order to work, improving performance. + if(!world.isRemote){ + + if(spell.cast(world, player, hand, 0, modifiers)){ + + // = Packets = + if(spell.doesSpellRequirePacket()){ + // Sends a packet to all players in dimension to tell them to spawn particles. + // Only sent if the spell succeeded, because if the spell failed, you wouldn't + // need to spawn any particles! + IMessage msg = new PacketCastSpell.Message(player.getEntityId(), hand, spell.id(), modifiers); + WizardryPacketHandler.net.sendToDimension(msg, world.provider.getDimension()); + } + + player.setActiveHand(hand); + + WizardData data = WizardData.get(player); + + // = Discovery = + if(data != null && !player.capabilities.isCreativeMode && !data.hasSpellBeenDiscovered(spell) && Wizardry.settings.discoveryMode){ + // We are only server side here, so we need to play the sound on the server side to everyone. + world.playSound(null, player.posX, player.posY, player.posZ, SoundEvents.ENTITY_PLAYER_LEVELUP, + SoundCategory.PLAYERS, 1.25f, 1); + player.addChatMessage(new TextComponentTranslation("spell.discover", spell.getNameForTranslationFormatted())); + } + WizardData.get(player).discoverSpell(spell); + + // = Cooldown = + // Spells only have a cooldown in survival + if(!player.capabilities.isCreativeMode){ + + float cooldownMultiplier = 1.0f - WandHelper.getUpgradeLevel(stack, WizardryItems.cooldown_upgrade)*Constants.COOLDOWN_REDUCTION_PER_LEVEL; + + if(player.isPotionActive(WizardryPotions.font_of_mana)){ + // Dividing by this rather than setting it takes upgrades and font of mana into account simultaneously + cooldownMultiplier /= 2 + player.getActivePotionEffect(WizardryPotions.font_of_mana).getAmplifier(); + } + + WandHelper.setCurrentCooldown(stack, (int)(spell.cooldown * cooldownMultiplier)); + } + + // = Mana cost = + // The spell costs 20% less for every armour piece of the matching element. + int armourPieces = getMatchingArmourCount(player, spell); + + stack.damageItem((int)(spell.cost * (1.0f - armourPieces*Constants.COST_REDUCTION_PER_ARMOUR)), player); + + return new ActionResult(EnumActionResult.SUCCESS, stack); + } + + // Client-inconsistent spell casting. The code inside the else if statement only runs client-side. + }else if(!spell.doesSpellRequirePacket()){ + // This is all that needs to happen, because everything above works fine on just the server side. + if(spell.cast(world, player, hand, 0, new SpellModifiers())){ + // Added in version 1.1.3 to fix the client-side spell discovery not updating for spells with the + // packet optimisation. + if(WizardData.get(player) != null){ + WizardData.get(player).discoverSpell(spell); + } + + new ActionResult(EnumActionResult.SUCCESS, stack); + } + } + } + + return new ActionResult(EnumActionResult.FAIL, stack); + } + + // For continuous spells. The count argument actually decrements by 1 each tick. + @Override + public void onUsingTick(ItemStack stack, EntityLivingBase user, int count){ + + if(user instanceof EntityPlayer){ + + EntityPlayer player = (EntityPlayer)user; + + Spell spell = WandHelper.getCurrentSpell(stack); + + WizardData data = WizardData.get(player); + + // Continuous spells (these must check if they can be cast each tick since the mana changes) + if(spell.isContinuous && spell.tier.level <= this.tier.level + && spell.cost/5 <= (stack.getMaxDamage() - stack.getItemDamage())){ + + // = Spell modifiers = + SpellModifiers modifiers = this.calculateModifiers(stack, spell); + + if(spell.cast(player.worldObj, player, player.getActiveHand(), stack.getMaxItemUseDuration() - count, modifiers)){ + + // = Discovery = + if(data != null && !player.capabilities.isCreativeMode && !data.hasSpellBeenDiscovered(spell) && Wizardry.settings.discoveryMode){ + player.playSound(SoundEvents.ENTITY_PLAYER_LEVELUP, 1.25f, 1); + if(!player.worldObj.isRemote) player.addChatMessage(new TextComponentTranslation("spell.discover", spell.getNameForTranslationFormatted())); + } + data.discoverSpell(spell); + + // = Mana cost = + // Divides the mana cost over a second appropriately; since damage is an integer it cannot + // just be divided by 20. + // Now does five times per second regardless of the spell cost, but each time it does 1/5 of the + // cost per second. + int tickNumber = (count % 20) + 1; + // Tests if the tick counter is a multiple of 4 plus 1, i.e. is true when tickNumber = 1, 5, 9, 13 or 17. + // Made a slight adjustment since the counter starts on 1 and not 4. + if(tickNumber % 4 == 1){ + + int armourPieces = getMatchingArmourCount(player, spell); + + switch(armourPieces){ + + case 0: stack.damageItem(spell.cost/5, player); + break; + + case 1: if(tickNumber != 17) stack.damageItem(spell.cost/5, player); + break; + + case 2: if(tickNumber != 9 && tickNumber != 17) stack.damageItem(spell.cost/5, player); + break; + + case 3: if(tickNumber != 5 && tickNumber != 13 && tickNumber != 17) stack.damageItem(spell.cost/5, player); + break; + + case 4: if(tickNumber == 1) stack.damageItem(spell.cost/5, player); + break; + + } + } + } + } + } + } + + /** Returns a SpellModifiers object with the appropriate modifiers applied for the given ItemStack and Spell. */ + protected SpellModifiers calculateModifiers(ItemStack stack, Spell spell){ + + SpellModifiers modifiers = new SpellModifiers(); + + // Now we only need to add multipliers if they are not 1. + int level = WandHelper.getUpgradeLevel(stack, WizardryItems.range_upgrade); + if(level > 0) modifiers.set(WizardryItems.range_upgrade, 1.0f + level * Constants.RANGE_INCREASE_PER_LEVEL, true); + + level = WandHelper.getUpgradeLevel(stack, WizardryItems.duration_upgrade); + if(level > 0) modifiers.set(WizardryItems.duration_upgrade, 1.0f + level * Constants.DURATION_INCREASE_PER_LEVEL, false); + + level = WandHelper.getUpgradeLevel(stack, WizardryItems.blast_upgrade); + if(level > 0) modifiers.set(WizardryItems.blast_upgrade, 1.0f + level * Constants.BLAST_RADIUS_INCREASE_PER_LEVEL, true); + + // I would have liked to have made potion effects increase in strength according to the damage multiplier, + // but the amplifier level is too discrete to make this work. For example, wither 3 for 10 seconds will kill a + // normal mob on full 20 health, but wither 2 for the same duration only deals about 6 hearts of damage in total. + if(this.element == spell.element){ + modifiers.set(SpellModifiers.DAMAGE, 1.0f + (this.tier.level + 1) * Constants.DAMAGE_INCREASE_PER_TIER, true); + } + + return modifiers; + } + + @Override + public boolean itemInteractionForEntity(ItemStack stack, EntityPlayer player, EntityLivingBase entity, EnumHand hand){ + + if(player.isSneaking() && entity instanceof EntityPlayer && WizardData.get(player) != null){ + // This is one of those "the method doing the work looks as if it's just returning a value" situations. + // ... I know, right?! I feel very programmer-y. But it's not too confusing here, and it looks neat. + String string = WizardData.get(player).toggleAlly((EntityPlayer)entity) ? "item.wizardry:wand.addally" : "item.wizardry:wand.removeally"; + if(!player.worldObj.isRemote) player.addChatMessage(new TextComponentTranslation(string, entity.getName())); + return true; + } + + return false; + } + + /** Counts the number of armour pieces the given player is wearing that match the given spell's element. */ + private int getMatchingArmourCount(EntityPlayer player, Spell spell){ + + int armourPieces = 0; + + for(EntityEquipmentSlot slot : WizardryUtilities.ARMOUR_SLOTS){ + + ItemStack armour = player.getItemStackFromSlot(slot); + + if(armour != null && armour.getItem() instanceof ItemWizardArmour + && ((ItemWizardArmour)armour.getItem()).element == spell.element) armourPieces++; + } + + return armourPieces; + } + + private boolean selectMinionTarget(EntityPlayer player, World world){ + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, player, 16); + + if(rayTrace != null && rayTrace.entityHit instanceof EntityLivingBase){ + + EntityLivingBase entity = (EntityLivingBase)rayTrace.entityHit; + + // Sets the selected minion's target to the right-clicked entity + if(player.isSneaking() && WizardData.get(player) != null && WizardData.get(player).selectedMinion != null){ + + ISummonedCreature minion = WizardData.get(player).selectedMinion.get(); + + if(minion instanceof EntityLiving && minion != entity){ + // There is now only the new AI! (which greatly improves things) + ((EntityLiving)minion).setAttackTarget(entity); + // Deselects the selected minion + WizardData.get(player).selectedMinion = null; + return true; + } + } + } + + return false; + } +} diff --git a/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java b/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java new file mode 100644 index 00000000..97a18fee --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java @@ -0,0 +1,217 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.spell.Petrify; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.EnumAction; +import net.minecraft.item.Item; +import net.minecraft.item.ItemArmor; +import net.minecraft.item.ItemStack; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumHandSide; +import net.minecraftforge.common.ISpecialArmor; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemWizardArmour extends ItemArmor implements ISpecialArmor { + + public Element element; + + public ItemWizardArmour(ArmorMaterial material, int renderIndex, EntityEquipmentSlot armourType, Element element) { + super(material, renderIndex, armourType); + this.element = element; + this.setCreativeTab(WizardryTabs.WIZARDRY); + } + + @Override + @SideOnly(Side.CLIENT) + public void addInformation(ItemStack stack, EntityPlayer player, List tooltip, boolean advanced){ + + if(stack.hasTagCompound() && stack.getTagCompound().getBoolean("legendary")) tooltip.add("\u00A7d" + net.minecraft.client.resources.I18n.format("item.wizardry:wizard_armour.legendary")); + if(element != null) tooltip.add("\u00A78" + net.minecraft.client.resources.I18n.format("item.wizardry:wizard_armour.buff", (int)(Constants.COST_REDUCTION_PER_ARMOUR*100) + "%", element.getDisplayName())); + tooltip.add("\u00A79" + net.minecraft.client.resources.I18n.format("item.wizardry:wizard_armour.mana", (this.getMaxDamage(stack) - this.getDamage(stack)), this.getMaxDamage(stack))); + } + + @Override + public String getItemStackDisplayName(ItemStack stack){ + return ((this.element == null ? "" : this.element.getFormattingCode()) + super.getItemStackDisplayName(stack)); + } + + @Override + @SideOnly(Side.CLIENT) + public boolean hasEffect(ItemStack stack){ + return stack.hasTagCompound() && stack.getTagCompound().getBoolean("legendary"); + } + + @Override + @SideOnly(Side.CLIENT) + public net.minecraft.client.model.ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack, EntityEquipmentSlot armourSlot, net.minecraft.client.model.ModelBiped _default){ + + net.minecraft.client.model.ModelBiped model = Wizardry.proxy.getWizardArmourModel(); + + // Legs use modelBiped + if(armourSlot == EntityEquipmentSlot.LEGS) return null; + + if(model != null){ + + model.bipedHead.showModel = armourSlot == EntityEquipmentSlot.HEAD; + model.bipedHeadwear.showModel = false; + model.bipedBody.showModel = armourSlot == EntityEquipmentSlot.CHEST || armourSlot == EntityEquipmentSlot.LEGS; + model.bipedRightArm.showModel = armourSlot == EntityEquipmentSlot.CHEST; + model.bipedLeftArm.showModel = armourSlot == EntityEquipmentSlot.CHEST; + model.bipedRightLeg.showModel = armourSlot == EntityEquipmentSlot.LEGS || armourSlot == EntityEquipmentSlot.FEET; + model.bipedLeftLeg.showModel = armourSlot == EntityEquipmentSlot.LEGS || armourSlot == EntityEquipmentSlot.FEET; + + model.isSneak = entityLiving.isSneaking(); + model.isRiding = entityLiving.isRiding(); + model.isChild = entityLiving.isChild(); + + boolean leftHanded = entityLiving.getPrimaryHand() == EnumHandSide.LEFT; + + ItemStack itemstackR = leftHanded ? entityLiving.getHeldItemOffhand() : entityLiving.getHeldItemMainhand(); + ItemStack itemstackL = leftHanded ? entityLiving.getHeldItemMainhand() : entityLiving.getHeldItemOffhand(); + + if (itemstackR != null) + { + model.rightArmPose = net.minecraft.client.model.ModelBiped.ArmPose.ITEM; + + if (entityLiving.getItemInUseCount() > 0) + { + EnumAction enumaction = itemstackR.getItemUseAction(); + + if (enumaction == EnumAction.BLOCK) + { + model.rightArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BLOCK; + } + else if (enumaction == EnumAction.BOW) + { + model.rightArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BOW_AND_ARROW; + } + } + } + + if (itemstackL != null) + { + model.leftArmPose = net.minecraft.client.model.ModelBiped.ArmPose.ITEM; + + if (entityLiving.getItemInUseCount() > 0) + { + EnumAction enumaction1 = itemstackL.getItemUseAction(); + + if (enumaction1 == EnumAction.BLOCK) + { + model.leftArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BLOCK; + } + else if (enumaction1 == EnumAction.BOW) + { + model.leftArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BOW_AND_ARROW; + } + } + } + } + + return model; + } + + @Override + public String getArmorTexture(ItemStack stack, Entity entity, EntityEquipmentSlot slot, String type) { + + // Returns a completely transparent texture if the player is invisible. This is such an annoyingly easy + // fix, considering how long I spent trying to do this before - a bit of lateral thinking was all it took. + // Do note however that a texture pack could override this. + if(entity instanceof EntityLivingBase && ((EntityLivingBase)entity).isInvisible() + && !entity.getEntityData().getBoolean(Petrify.NBT_KEY)) return "wizardry:textures/armour/invisible_armour.png"; + + if(slot == EntityEquipmentSlot.LEGS) return this.element == null ? "wizardry:textures/armour/wizard_armour_legs.png" : + "wizardry:textures/armour/wizard_armour_" + this.element.getUnlocalisedName() + "_legs.png"; + + return this.element == null ? "wizardry:textures/armour/wizard_armour.png" : + "wizardry:textures/armour/wizard_armour_" + this.element.getUnlocalisedName() + ".png"; + } + + @Override + public boolean getIsRepairable(ItemStack stack, ItemStack par2ItemStack) + { + return false; + } + + @Override + public ArmorProperties getProperties(EntityLivingBase player, ItemStack armor, DamageSource source, double damage, int slotIndex){ + + EntityEquipmentSlot slot = getArmorSlotFromIndex(slotIndex); + + if (!source.isUnblockable() && armor.getItemDamage() < armor.getMaxDamage()){ + if(armor.hasTagCompound() && armor.getTagCompound().getBoolean("legendary")){ + // Legendary armour gives full 10 shields, like diamond. + return new ArmorProperties(0, ArmorMaterial.DIAMOND.getDamageReductionAmount(slot) / 25D, armor.getMaxDamage() + 1 - armor.getItemDamage()); + }else{ + return new ArmorProperties(0, this.damageReduceAmount / 25D, armor.getMaxDamage() + 1 - armor.getItemDamage()); + } + }else{ + return new ArmorProperties(0, 0, 0); + } + } + + @Override + public int getArmorDisplay(EntityPlayer player, ItemStack armor, int slotIndex) { + + EntityEquipmentSlot slot = getArmorSlotFromIndex(slotIndex); + + if(armor.getItemDamage() < armor.getMaxDamage()){ + if(armor.hasTagCompound() && armor.getTagCompound().getBoolean("legendary")){ + // Legendary armour gives full 10 shields, like diamond. + return ArmorMaterial.DIAMOND.getDamageReductionAmount(slot); + }else{ + return this.getArmorMaterial().getDamageReductionAmount(slot); + } + }else{ + return 0; + } + } + + @Override + public void damageArmor(EntityLivingBase entity, ItemStack stack, DamageSource source, int damage, int slot){ + if(stack.getItemDamage() < stack.getMaxDamage()){ + if(stack.getItemDamage() + damage > stack.getMaxDamage()){ + // Note for reference: this is the method to use, despite how it sounds attemptDamageItem() is called + // by this one, not the other way round. + stack.damageItem(stack.getMaxDamage() - stack.getItemDamage(), entity); + }else{ + stack.damageItem(damage, entity); + } + } + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item item, CreativeTabs tab, List subItems){ + subItems.add(new ItemStack(this, 1)); + } + + /** Returns the EntityEquipmentSlot for which index() returns an integer equal to the passed in slotIndex and + * which is an armour slot (not a hand slot). This only exists because the 1.10.2 version of Forge still uses + * an integer slot index in ISpecialArmor instead of an EntityEquipmentSlot. */ + // NOTE: Remove in 1.11.2 + private EntityEquipmentSlot getArmorSlotFromIndex(int slotIndex) { + // Had to pick something to begin with. + EntityEquipmentSlot slot = EntityEquipmentSlot.HEAD; + + for(EntityEquipmentSlot s : WizardryUtilities.ARMOUR_SLOTS){ + if(s.getIndex() == slotIndex){ + slot = s; + } + } + return slot; + } + +} diff --git a/src/main/java/electroblob/wizardry/item/ItemWizardHandbook.java b/src/main/java/electroblob/wizardry/item/ItemWizardHandbook.java new file mode 100644 index 00000000..3b7060db --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemWizardHandbook.java @@ -0,0 +1,46 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryGuiHandler; +import electroblob.wizardry.registry.WizardryTabs; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class ItemWizardHandbook extends Item { + + // Yep, I hardcoded my own name into the mod. Don't want people changing it now, do I? + private static final String AUTHOR = "Electroblob"; + + public ItemWizardHandbook() { + super(); + this.setMaxStackSize(1); + this.setCreativeTab(WizardryTabs.WIZARDRY); + } + + @Override + public void addInformation(ItemStack stack, EntityPlayer player, List tooltip, boolean advanced){ + tooltip.add("\u00A77" + net.minecraft.client.resources.I18n.format("item.wizardry:wizard_handbook.desc", AUTHOR)); + } + + @Override + public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ + player.openGui(Wizardry.instance, WizardryGuiHandler.WIZARD_HANDBOOK, world, 0, 0, 0); + return ActionResult.newResult(EnumActionResult.SUCCESS, stack); + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item item, CreativeTabs tab, List items){ + items.add(new ItemStack(this, 1)); + } +} diff --git a/src/main/java/electroblob/wizardry/loot/RandomSpell.java b/src/main/java/electroblob/wizardry/loot/RandomSpell.java new file mode 100644 index 00000000..90c7c661 --- /dev/null +++ b/src/main/java/electroblob/wizardry/loot/RandomSpell.java @@ -0,0 +1,259 @@ +package electroblob.wizardry.loot; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSyntaxException; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.item.ItemScroll; +import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.item.ItemStack; +import net.minecraft.util.JsonUtils; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.storage.loot.LootContext; +import net.minecraft.world.storage.loot.conditions.LootCondition; +import net.minecraft.world.storage.loot.functions.LootFunction; + +/** Loot function that allows spell books and scrolls to select a random spell based on the standard weighting. + * Automatically discounts continuous spells when the item in question is a scroll. Can be used as-is with no + * parameters, but several optional parameters are available for those wishing to customise further: + *

    + * - spells: A list of spells to choose from. Defaults to all enabled spells.
    + * - ignore_weighting: true to ignore the standard weighting and just pick a completely random spell. Defaults to + * false.
    + * - tiers: A list of tiers to choose from. Defaults to all tiers.
    + * - elements: A list of elements to choose from. Defaults to all elements. + *

    + * This class is effectively a loot table-friendly replacement for the standard weighting method in WizardryUtilities + * which was the basis of all the old loot systems (not counting wizard trades, which were - and still are - completely + * separate). + *

    + * Since spells are stored as metadata, this could be done by having an entry for each tier and letting it pick a + * random spell from that tier by setting the metadata to a random value in the range of values that correspond to that + * tier. However, this creates a two-fold problem: firstly, not all spells are in tier order, and secondly, if spells are + * added via addon mods the entries would have to be updated manually with the new numbers. + *

    + * (This reasoning is similar to that for the enchant_randomly function in vanilla Minecraft, since you can specify NBT + * data in loot tables, but using NBT in this way would be incredibly verbose and inflexible, hence the loot function.) + * @see WizardryUtilities#getStandardWeightedRandomSpellId(Random, boolean) + * @author Electroblob + * @since Wizardry 1.2 + */ +/* + * You could even use the yet more long-winded method of adding each spell as an individual entry, which would be + * stupidly long but would allow precise control over each individual spell... I'll leave that for pack makers to do + * if they have the patience! + * */ +public class RandomSpell extends LootFunction { + + private final List spells; + private final boolean ignoreWeighting; + private final List tiers; + private final List elements; + + protected RandomSpell(LootCondition[] conditions, List spells, boolean ignoreWeighting, List tiers, List elements){ + super(conditions); + this.spells = spells; + this.ignoreWeighting = ignoreWeighting; + this.tiers = tiers; + this.elements = elements; + } + + @Override + public ItemStack apply(ItemStack stack, Random random, LootContext context){ + + if(!(stack.getItem() instanceof ItemSpellBook) || !(stack.getItem() instanceof ItemScroll)) + Wizardry.logger.warn("Applying the random_spell loot function to an item that isn't a spell book or scroll."); + + Tier tier; + Element element; + + if(ignoreWeighting){ + if(tiers == null || tiers.isEmpty()){ + tier = Tier.values()[random.nextInt(Tier.values().length)]; + }else{ + tier = tiers.get(random.nextInt(tiers.size())); + } + }else{ + if(tiers == null || tiers.isEmpty()){ + tier = Tier.getWeightedRandomTier(random); + }else{ + tier = Tier.getWeightedRandomTier(random, tiers.toArray(new Tier[0])); + } + } + + // Elements aren't weighted + if(elements == null || elements.isEmpty()){ + element = Element.values()[random.nextInt(Element.values().length)]; + }else{ + // In theory, swapping this line to the commented one should make absolutely no difference. + element = elements.get(random.nextInt(tiers.size())); + //element = null; + } + + // Here's a thought: does randomly selecting the element beforehand (as opposed to leaving it null and letting + // the spell randomiser use any element) change the overall outcome at all? + List spellsList = Spell.getSpells(new Spell.TierElementFilter(tier, element)); + if(stack.getItem() instanceof ItemScroll) spellsList.retainAll(Spell.getSpells(Spell.nonContinuousSpells)); + + // Ensures the tier chosen actually has spells in it, and if not uses BASIC instead. BASIC always has at least + // the NONE spell since this spell cannot be disabled. + // NOTE: Commented out for now because it will interfere with the ability to specify tiers and elements. + // To be honest, I may as well just say that if you disable enough spells to make this important, you deserve + // less loot! + /* + if(spells.isEmpty()){ + spellsList = Spell.getSpells(new Spell.TierElementFilter(EnumTier.BASIC, null)); + if(stack.getItem() instanceof ItemScroll) spellsList.retainAll(Spell.getSpells(Spell.nonContinuousSpells)); + } + */ + + if(spells != null && !spells.isEmpty()){ + spellsList.retainAll(spells); + } + + if(spellsList.isEmpty()){ + Wizardry.logger.warn("Tried to apply the random_spell loot function to an item, but no enabled spells" + + "matched the criteria specified. Substituting placeholder (metadata 0) item."); + stack.setItemDamage(0); + }else{ + stack.setItemDamage(spellsList.get(random.nextInt(spellsList.size())).id()); + } + + return stack; + } + + public static class Serializer extends LootFunction.Serializer{ + + public Serializer(){ + super(new ResourceLocation(Wizardry.MODID, "random_spell"), RandomSpell.class); + } + + public void serialize(JsonObject object, RandomSpell function, JsonSerializationContext serializationContext){ + + if(function.spells != null && !function.spells.isEmpty()){ + + JsonArray jsonarray = new JsonArray(); + + for(Spell spell : function.spells){ + jsonarray.add(new JsonPrimitive(spell.getRegistryName().toString())); + } + + object.add("spells", jsonarray); + } + + object.addProperty("ignore_weighting", function.ignoreWeighting); + + if(function.tiers != null && !function.tiers.isEmpty()){ + + JsonArray jsonarray = new JsonArray(); + + for(Tier tier : function.tiers){ + jsonarray.add(new JsonPrimitive(tier.getUnlocalisedName())); + } + + object.add("tiers", jsonarray); + } + + if(function.elements != null && !function.elements.isEmpty()){ + + JsonArray jsonarray = new JsonArray(); + + for(Element element : function.elements){ + jsonarray.add(new JsonPrimitive(element.getUnlocalisedName())); + } + + object.add("elements", jsonarray); + } + } + + public RandomSpell deserialize(JsonObject object, JsonDeserializationContext deserializationContext, LootCondition[] conditions){ + + List spells = null; + List tiers = null; + List elements = null; + + if(object.has("spells")){ + + // Vanilla Minecraft calls Lists.newArrayList() here, which is completely pointless. + spells = new ArrayList(); + + // Importantly, it is necessary to specify a default (the new JsonArray) here because otherwise the + // parameter will be mandatory, and the game will crash if it isn't present. + for (JsonElement element : JsonUtils.getJsonArray(object, "spells", new JsonArray())){ + + String string = JsonUtils.getString(element, "spell"); + + Spell spell = Spell.get(string); + + if(spell == null){ + throw new JsonSyntaxException("Unknown spell \'" + string + "\'"); + } + + spells.add(spell); + } + } + + boolean ignoreWeighting = JsonUtils.getBoolean(object, "ignore_weighting", false); + + if(object.has("tiers")){ + + // Vanilla Minecraft calls Lists.newArrayList() here, which is completely pointless. + tiers = new ArrayList(); + + jsonarray: + for(JsonElement element : JsonUtils.getJsonArray(object, "tiers", new JsonArray())){ + + String string = JsonUtils.getString(element, "tier"); + + // IDEA: If it is necessary to get tiers by name elsewhere in the future, move this to EnumTier. + for(Tier tier : Tier.values()){ + if(tier.getUnlocalisedName().equals(string)){ + tiers.add(tier); + continue jsonarray; + } + } + // If the string does not match any of the tiers, throws an exception. + throw new JsonSyntaxException("Unknown tier \'" + string + "\'"); + } + } + + if(object.has("elements")){ + + // Vanilla Minecraft calls Lists.newArrayList() here, which is completely pointless. + elements = new ArrayList(); + + jsonarray: + for(JsonElement jelement : JsonUtils.getJsonArray(object, "elements", new JsonArray())){ + + String string = JsonUtils.getString(jelement, "element"); + + // IDEA: If it is necessary to get elements by name elsewhere in the future, move this to EnumElement. + for(Element element : Element.values()){ + if(element.getUnlocalisedName().equals(string)){ + elements.add(element); + continue jsonarray; + } + } + // If the string does not match any of the elements, throws an exception. + throw new JsonSyntaxException("Unknown element \'" + string + "\'"); + } + } + + return new RandomSpell(conditions, spells, ignoreWeighting, tiers, elements); + } + } + +} diff --git a/src/main/java/electroblob/wizardry/loot/WizardSpell.java b/src/main/java/electroblob/wizardry/loot/WizardSpell.java new file mode 100644 index 00000000..c52898b5 --- /dev/null +++ b/src/main/java/electroblob/wizardry/loot/WizardSpell.java @@ -0,0 +1,65 @@ +package electroblob.wizardry.loot; + +import java.util.List; +import java.util.Random; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.living.ISpellCaster; +import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.spell.Spell; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.storage.loot.LootContext; +import net.minecraft.world.storage.loot.conditions.LootCondition; +import net.minecraft.world.storage.loot.functions.LootFunction; + +/** + * Loot function that allows spell books to select a random spell from the spells used by the ISpellCaster that dropped + * them. + * @author Electroblob + * @since Wizardry 1.2 + */ +public class WizardSpell extends LootFunction { + + protected WizardSpell(LootCondition[] conditions){ + super(conditions); + } + + @Override + public ItemStack apply(ItemStack stack, Random random, LootContext context){ + + if(!(stack.getItem() instanceof ItemSpellBook)) + Wizardry.logger.warn("Applying the wizard_spell loot function to an item that isn't a spell book or scroll."); + + if(context.getLootedEntity() instanceof ISpellCaster){ + List spells = ((ISpellCaster)context.getLootedEntity()).getSpells(); + spells.remove(Spells.magic_missile); // Can't drop magic missile + stack.setItemDamage(spells.get(random.nextInt(spells.size())).id()); + }else{ + Wizardry.logger.warn("Applying the wizard_spell loot function to an entity that isn't a spell caster."); + } + + return stack; + } + + public static class Serializer extends LootFunction.Serializer{ + + public Serializer(){ + super(new ResourceLocation(Wizardry.MODID, "wizard_spell"), WizardSpell.class); + } + + public void serialize(JsonObject object, WizardSpell function, JsonSerializationContext serializationContext){ + + } + + public WizardSpell deserialize(JsonObject object, JsonDeserializationContext deserializationContext, LootCondition[] conditions){ + return new WizardSpell(conditions); + } + } + +} diff --git a/src/main/java/electroblob/wizardry/packet/PacketCastContinuousSpell.java b/src/main/java/electroblob/wizardry/packet/PacketCastContinuousSpell.java new file mode 100644 index 00000000..9ef22bae --- /dev/null +++ b/src/main/java/electroblob/wizardry/packet/PacketCastContinuousSpell.java @@ -0,0 +1,70 @@ +package electroblob.wizardry.packet; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.packet.PacketCastContinuousSpell.Message; +import electroblob.wizardry.util.SpellModifiers; +import io.netty.buffer.ByteBuf; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; + +/** [Server -> Client] This packet is sent when the /cast command is used with a continuous spell, in order to + * sync the relevant variables in {@link electroblob.wizardry.WizardData WizardData}. */ +public class PacketCastContinuousSpell implements IMessageHandler { + + @Override + public IMessage onMessage(Message message, MessageContext ctx){ + + // Just to make sure that the side is correct + if(ctx.side.isClient()){ + // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy + // methods any more than necessary. + net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ + @Override + public void run() { + Wizardry.proxy.handleCastContinuousSpellPacket(message); + } + }); + } + + return null; + } + + public static class Message implements IMessage { + + // Note that range and blast multipliers are the only two that affect particle spawning, so they are the + // only two that need to be sent. + + /** EntityID of the caster */ + public int casterID; + /** ID of the spell being cast */ + public int spellID; + /** SpellModifiers for the spell */ + public SpellModifiers modifiers; + + // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) + public Message(){} + + public Message(int casterID, int spellID, SpellModifiers modifiers){ + this.casterID = casterID; + this.spellID = spellID; + this.modifiers = modifiers; + } + + @Override + public void fromBytes(ByteBuf buf){ + // The order is important + this.casterID = buf.readInt(); + this.spellID = buf.readInt(); + this.modifiers = new SpellModifiers(); + modifiers.read(buf); + } + + @Override + public void toBytes(ByteBuf buf){ + buf.writeInt(casterID); + buf.writeInt(spellID); + this.modifiers.write(buf); + } + } +} diff --git a/src/main/java/electroblob/wizardry/packet/PacketCastSpell.java b/src/main/java/electroblob/wizardry/packet/PacketCastSpell.java new file mode 100644 index 00000000..cf57872c --- /dev/null +++ b/src/main/java/electroblob/wizardry/packet/PacketCastSpell.java @@ -0,0 +1,80 @@ +package electroblob.wizardry.packet; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.packet.PacketCastSpell.Message; +import electroblob.wizardry.util.SpellModifiers; +import io.netty.buffer.ByteBuf; +import net.minecraft.item.Item; +import net.minecraft.util.EnumHand; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; + +/** [Server -> Client] This packet is sent when a spell is cast by a player and returns true, and is sent to other + * clients so they can spawn the particles themselves. What sending this packet effectively does is make the + * {@link Item#onItemRightClick} method client-consistent just for the item that sends it. Interestingly, + * {@link Item#onUsingTick} is client-consistent already, so continuous spells don't need to send packets from there + * (this is probably something to do with eating particles or usage actions). */ +public class PacketCastSpell implements IMessageHandler { + + @Override + public IMessage onMessage(Message message, MessageContext ctx){ + + // Just to make sure that the side is correct + if(ctx.side.isClient()){ + // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy + // methods any more than necessary. + net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ + @Override + public void run(){ + Wizardry.proxy.handleCastSpellPacket(message); + } + }); + } + + return null; + } + + public static class Message implements IMessage { + + /** EntityID of the caster */ + public int casterID; + /** ID of the spell being cast */ + public int spellID; + /** SpellModifiers for the spell */ + public SpellModifiers modifiers; + /** The hand that is holding the itemstack used to cast the spell. Defaults to MAIN_HAND. */ + public EnumHand hand; + + // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) + public Message(){} + + public Message(int casterID, EnumHand hand, int spellID, SpellModifiers modifiers){ + + this.casterID = casterID; + this.spellID = spellID; + this.modifiers = modifiers; + this.hand = hand == null ? EnumHand.MAIN_HAND : hand; + } + + @Override + public void fromBytes(ByteBuf buf){ + + // The order is important + this.casterID = buf.readInt(); + this.spellID = buf.readInt(); + this.modifiers = new SpellModifiers(); + this.modifiers.read(buf); + this.hand = buf.readBoolean() ? EnumHand.OFF_HAND : EnumHand.MAIN_HAND; + } + + @Override + public void toBytes(ByteBuf buf){ + + buf.writeInt(casterID); + buf.writeInt(spellID); + this.modifiers.write(buf); + buf.writeBoolean(this.hand == EnumHand.OFF_HAND); + } + } +} diff --git a/src/main/java/electroblob/wizardry/packet/PacketClairvoyance.java b/src/main/java/electroblob/wizardry/packet/PacketClairvoyance.java new file mode 100644 index 00000000..f7f350bd --- /dev/null +++ b/src/main/java/electroblob/wizardry/packet/PacketClairvoyance.java @@ -0,0 +1,81 @@ +package electroblob.wizardry.packet; + +import java.util.ArrayList; +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.packet.PacketClairvoyance.Message; +import io.netty.buffer.ByteBuf; +import net.minecraft.pathfinding.Path; +import net.minecraft.pathfinding.PathPoint; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; + +/** [Server -> Client] This packet is sent when a player casts the clairvoyance spell to allow pathing to chunks + * outside the render distance. */ +public class PacketClairvoyance implements IMessageHandler { + + @Override + public IMessage onMessage(Message message, MessageContext ctx) + { + // Just to make sure that the side is correct + if(ctx.side.isClient()){ + // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy + // methods any more than necessary. + net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ + @Override + public void run(){ + Wizardry.proxy.handleClairvoyancePacket(message); + } + }); + } + + return null; + } + + public static class Message implements IMessage { + + public Path path; + public float durationMultiplier; + + // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) + public Message() {} + + public Message(Path path, float durationMultiplier){ + + this.path = path; + this.durationMultiplier = durationMultiplier; + } + + @Override + public void fromBytes(ByteBuf buf){ + + // The order is important + this.durationMultiplier = buf.readFloat(); + + List points = new ArrayList(); + + while(buf.isReadable()){ + points.add(new PathPoint(buf.readInt(), buf.readInt(), buf.readInt())); + } + + this.path = new Path(points.toArray(new PathPoint[0])); + } + + @Override + public void toBytes(ByteBuf buf){ + + buf.writeFloat(durationMultiplier); + + for(int i=0; i[Client -> Server] This packet is for control events such as buttons in GUIs and key presses. */ +public class PacketControlInput implements IMessageHandler { + + @Override + public IMessage onMessage(Message message, MessageContext ctx){ + + // Just to make sure that the side is correct + if(ctx.side.isServer()){ + + final EntityPlayerMP player = ctx.getServerHandler().playerEntity; + + player.getServerWorld().addScheduledTask(new Runnable(){ + + public void run() { + + ItemStack wand = player.getHeldItemMainhand(); + + if(wand == null || !(wand.getItem() instanceof ItemWand)){ + wand = player.getHeldItemOffhand(); + } + + switch(message.controlType){ + + case APPLY_BUTTON: + + TileEntityArcaneWorkbench tileentity = ((ContainerArcaneWorkbench)player.openContainer).tileEntityArcaneWorkbench; + tileentity.onApplyButtonPressed(player); + break; + + case NEXT_SPELL_KEY: + + if(wand != null && wand.getItem() instanceof ItemWand){ + + WandHelper.selectNextSpell(wand); + // This line fixes the bug with continuous spells casting when they shouldn't be + player.stopActiveHand(); + } + + break; + + case PREVIOUS_SPELL_KEY: + + if(wand != null && wand.getItem() instanceof ItemWand){ + + WandHelper.selectPreviousSpell(wand); + // This line fixes the bug with continuous spells casting when they shouldn't be + player.stopActiveHand(); + } + + break; + } + } + } + ); + } + + return null; + } + + public static enum ControlType { + APPLY_BUTTON, + NEXT_SPELL_KEY, + PREVIOUS_SPELL_KEY; + } + + public static class Message implements IMessage { + + private ControlType controlType; + + // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) + public Message() {} + + public Message(ControlType type){ + this.controlType = type; + } + + @Override + public void fromBytes(ByteBuf buf){ + // The order is important + this.controlType = ControlType.values()[buf.readInt()]; + } + + @Override + public void toBytes(ByteBuf buf){ + buf.writeInt(controlType.ordinal()); + } + } +} diff --git a/src/main/java/electroblob/wizardry/packet/PacketGlyphData.java b/src/main/java/electroblob/wizardry/packet/PacketGlyphData.java new file mode 100644 index 00000000..7f7191e5 --- /dev/null +++ b/src/main/java/electroblob/wizardry/packet/PacketGlyphData.java @@ -0,0 +1,70 @@ +package electroblob.wizardry.packet; + +import java.util.ArrayList; +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.packet.PacketGlyphData.Message; +import io.netty.buffer.ByteBuf; +import net.minecraftforge.fml.common.network.ByteBufUtils; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; + +/** [Server -> Client] This packet is sent each time a player joins a world to synchronise the client-side spell + * glyph names with the server-side ones. */ +public class PacketGlyphData implements IMessageHandler { + + @Override + public IMessage onMessage(Message message, MessageContext ctx) + { + // Just to make sure that the side is correct + if(ctx.side.isClient()){ + // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy + // methods any more than necessary. + net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ + @Override + public void run(){ + Wizardry.proxy.handleGlyphDataPacket(message); + } + }); + } + + return null; + } + + public static class Message implements IMessage { + + /** The list of randomised spell names. */ + public List names; + /** The list of randomised spell descriptions. */ + public List descriptions; + + // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) + public Message() {} + + public Message(List names, List descriptions) + { + this.names = names; + this.descriptions = descriptions; + } + + @Override + public void fromBytes(ByteBuf buf){ + names = new ArrayList(); + descriptions = new ArrayList(); + while(buf.isReadable()){ + names.add(ByteBufUtils.readUTF8String(buf)); + descriptions.add(ByteBufUtils.readUTF8String(buf)); + } + } + + @Override + public void toBytes(ByteBuf buf){ + for(int i=0; i[Server -> Client] This packet is sent when an {@link ISpellCaster} casts a spell which returns true, and is + * sent to clients so they can spawn the particles. Unlike the player packets, this is for both continuous and + * non-continuous spells. */ +public class PacketNPCCastSpell implements IMessageHandler { + + @Override + public IMessage onMessage(Message message, MessageContext ctx){ + + // Just to make sure that the side is correct + if(ctx.side.isClient()){ + // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy + // methods any more than necessary. + net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ + @Override + public void run(){ + Wizardry.proxy.handleNPCCastSpellPacket(message); + } + }); + } + + return null; + } + + public static class Message implements IMessage { + + /** EntityID of the caster */ + public int casterID; + /** EntityID of the target, or -1 if there is none */ + public int targetID; + /** ID of the spell being cast */ + public int spellID; + /** SpellModifiers for the spell */ + public SpellModifiers modifiers; + /** The hand that is holding the itemstack used to cast the spell. Defaults to MAIN_HAND. */ + public EnumHand hand; + + // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) + public Message(){} + + public Message(int casterID, int targetID, EnumHand hand, int spellID, SpellModifiers modifiers){ + this.casterID = casterID; + this.targetID = targetID; + this.spellID = spellID; + this.modifiers = modifiers; + this.hand = hand == null ? EnumHand.MAIN_HAND : hand; + } + + @Override + public void fromBytes(ByteBuf buf){ + // The order is important + this.casterID = buf.readInt(); + this.targetID = buf.readInt(); + this.spellID = buf.readInt(); + this.modifiers = new SpellModifiers(); + this.modifiers.read(buf); + this.hand = buf.readBoolean() ? EnumHand.OFF_HAND : EnumHand.MAIN_HAND; + } + + @Override + public void toBytes(ByteBuf buf){ + buf.writeInt(casterID); + buf.writeInt(targetID); + buf.writeInt(spellID); + this.modifiers.write(buf); + buf.writeBoolean(this.hand == EnumHand.OFF_HAND); + } + } +} diff --git a/src/main/java/electroblob/wizardry/packet/PacketPlayerSync.java b/src/main/java/electroblob/wizardry/packet/PacketPlayerSync.java new file mode 100644 index 00000000..d58dfbe4 --- /dev/null +++ b/src/main/java/electroblob/wizardry/packet/PacketPlayerSync.java @@ -0,0 +1,71 @@ +package electroblob.wizardry.packet; + +import java.util.HashSet; +import java.util.Set; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.packet.PacketPlayerSync.Message; +import electroblob.wizardry.spell.Spell; +import io.netty.buffer.ByteBuf; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; + +/** [Server -> Client] This packet is sent to synchronise any fields that need synchronising in + * {@link electroblob.wizardry.WizardData WizardData}. This packet is not sent often enough and is too small to warrant + * having separate packets for each field that needs synchronising. */ +public class PacketPlayerSync implements IMessageHandler { + + @Override + public IMessage onMessage(Message message, MessageContext ctx) + { + // Just to make sure that the side is correct + if(ctx.side.isClient()){ + // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy + // methods any more than necessary. + net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ + @Override + public void run(){ + Wizardry.proxy.handlePlayerSyncPacket(message); + } + }); + } + + return null; + } + + public static class Message implements IMessage { + + public Set spellsDiscovered; + public int selectedMinionID; + + // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) + public Message() {} + + public Message(Set spellsDiscovered2, int selectedMinionID){ + this.spellsDiscovered = spellsDiscovered2; + this.selectedMinionID = selectedMinionID; + } + + @Override + public void fromBytes(ByteBuf buf){ + this.selectedMinionID = buf.readInt(); + this.spellsDiscovered = new HashSet(); + while(buf.isReadable()){ + this.spellsDiscovered.add(Spell.get(buf.readInt())); + } + } + + @Override + public void toBytes(ByteBuf buf){ + + buf.writeInt(selectedMinionID); + + if(this.spellsDiscovered == null) return; + + for(Spell spell : this.spellsDiscovered){ + buf.writeInt(spell.id()); + } + } + } +} diff --git a/src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java b/src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java new file mode 100644 index 00000000..bed891c6 --- /dev/null +++ b/src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java @@ -0,0 +1,90 @@ +package electroblob.wizardry.packet; + +import electroblob.wizardry.Settings; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.packet.PacketSyncSettings.Message; +import io.netty.buffer.ByteBuf; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; + +/** [Server -> Client] This packet is sent to synchronise the config settings with clients on player login. + * @see Settings */ +public class PacketSyncSettings implements IMessageHandler { + + @Override + public IMessage onMessage(Message message, MessageContext ctx){ + + if(ctx.side.isClient()){ + // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy + // any more than necessary. + net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ + @Override + public void run(){ + copySettings(message); + } + }); + + } + + return null; + } + + private static void copySettings(Message message){ + Wizardry.settings.firebombIsCraftable = message.settings.firebombIsCraftable; + Wizardry.settings.poisonBombIsCraftable = message.settings.poisonBombIsCraftable; + Wizardry.settings.smokeBombIsCraftable = message.settings.smokeBombIsCraftable; + Wizardry.settings.useAlternateScrollRecipe = message.settings.useAlternateScrollRecipe; + Wizardry.settings.discoveryMode = message.settings.discoveryMode; +// Wizardry.settings.maxSpellCommandMultiplier = message.settings.maxSpellCommandMultiplier; +// Wizardry.settings.castCommandName = message.settings.castCommandName; +// Wizardry.settings.discoverspellCommandName = message.settings.discoverspellCommandName; +// Wizardry.settings.allyCommandName = message.settings.allyCommandName; +// Wizardry.settings.alliesCommandName = message.settings.alliesCommandName; + } + + public static class Message implements IMessage { + + /** EntityID of the caster */ + public Settings settings; + + // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) + public Message(){} + + public Message(Settings settings){ + this.settings = settings; + } + + @Override + public void fromBytes(ByteBuf buf){ + // I'm guessing the settings field will be null here, so it needs initialising. + // This is also a great reason to have the settings as an actual object. + settings = new Settings(); + // The order is important + settings.firebombIsCraftable = buf.readBoolean(); + settings.poisonBombIsCraftable = buf.readBoolean(); + settings.smokeBombIsCraftable = buf.readBoolean(); + settings.useAlternateScrollRecipe = buf.readBoolean(); + settings.discoveryMode = buf.readBoolean(); +// settings.maxSpellCommandMultiplier = buf.readDouble(); +// settings.castCommandName = ByteBufUtils.readUTF8String(buf); +// settings.discoverspellCommandName = ByteBufUtils.readUTF8String(buf); +// settings.allyCommandName = ByteBufUtils.readUTF8String(buf); +// settings.alliesCommandName = ByteBufUtils.readUTF8String(buf); + } + + @Override + public void toBytes(ByteBuf buf){ + buf.writeBoolean(settings.firebombIsCraftable); + buf.writeBoolean(settings.poisonBombIsCraftable); + buf.writeBoolean(settings.smokeBombIsCraftable); + buf.writeBoolean(settings.useAlternateScrollRecipe); + buf.writeBoolean(settings.discoveryMode); +// buf.writeDouble(settings.maxSpellCommandMultiplier); +// ByteBufUtils.writeUTF8String(buf, settings.castCommandName); +// ByteBufUtils.writeUTF8String(buf, settings.discoverspellCommandName); +// ByteBufUtils.writeUTF8String(buf, settings.allyCommandName); +// ByteBufUtils.writeUTF8String(buf, settings.alliesCommandName); + } + } +} diff --git a/src/main/java/electroblob/wizardry/packet/PacketTransportation.java b/src/main/java/electroblob/wizardry/packet/PacketTransportation.java new file mode 100644 index 00000000..7a7085c8 --- /dev/null +++ b/src/main/java/electroblob/wizardry/packet/PacketTransportation.java @@ -0,0 +1,58 @@ +package electroblob.wizardry.packet; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.packet.PacketTransportation.Message; +import io.netty.buffer.ByteBuf; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; + +/** [Server -> Client] This packet is sent when a player is teleported due to the transportation spell to spawn + * the particles. */ +public class PacketTransportation implements IMessageHandler { + + @Override + public IMessage onMessage(Message message, MessageContext ctx) + { + // Just to make sure that the side is correct + if(ctx.side.isClient()){ + // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy + // methods any more than necessary. + net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ + @Override + public void run(){ + Wizardry.proxy.handleTransportationPacket(message); + } + }); + } + + return null; + } + + public static class Message implements IMessage + { + /** EntityID of the caster */ + public int casterID; + + // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) + public Message() {} + + public Message(int casterID) + { + this.casterID = casterID; + } + + @Override + public void fromBytes(ByteBuf buf) + { + // The order is important + this.casterID = buf.readInt(); + } + + @Override + public void toBytes(ByteBuf buf) + { + buf.writeInt(casterID); + } + } +} diff --git a/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java b/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java new file mode 100644 index 00000000..dedaead6 --- /dev/null +++ b/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java @@ -0,0 +1,36 @@ +package electroblob.wizardry.packet; + +import electroblob.wizardry.Wizardry; +import net.minecraftforge.fml.common.network.NetworkRegistry; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper; +import net.minecraftforge.fml.relauncher.Side; + +public class WizardryPacketHandler { + + public static SimpleNetworkWrapper net; + + public static void initPackets() + { + net = NetworkRegistry.INSTANCE.newSimpleChannel(Wizardry.MODID.toUpperCase()); + registerMessage(PacketControlInput.class, PacketControlInput.Message.class); + registerMessage(PacketCastSpell.class, PacketCastSpell.Message.class); + registerMessage(PacketTransportation.class, PacketTransportation.Message.class); + registerMessage(PacketPlayerSync.class, PacketPlayerSync.Message.class); + registerMessage(PacketGlyphData.class, PacketGlyphData.Message.class); + registerMessage(PacketCastContinuousSpell.class, PacketCastContinuousSpell.Message.class); + registerMessage(PacketClairvoyance.class, PacketClairvoyance.Message.class); + registerMessage(PacketSyncSettings.class, PacketSyncSettings.Message.class); + registerMessage(PacketNPCCastSpell.class, PacketNPCCastSpell.Message.class); + } + + private static int nextPacketId = 0; + + private static void registerMessage(Class> packet, Class message) + { + net.registerMessage(packet, message, nextPacketId, Side.CLIENT); + net.registerMessage(packet, message, nextPacketId, Side.SERVER); + nextPacketId++; + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/potion/ICustomPotionParticles.java b/src/main/java/electroblob/wizardry/potion/ICustomPotionParticles.java new file mode 100644 index 00000000..4b66e59d --- /dev/null +++ b/src/main/java/electroblob/wizardry/potion/ICustomPotionParticles.java @@ -0,0 +1,23 @@ +package electroblob.wizardry.potion; + +import net.minecraft.world.World; + +/** + * Interface for potion effects that spawn custom particles instead of (or as well as) the vanilla 'swirly' particles. + * @author Electroblob + * @since Wizardry 1.2 + */ +// TODO: Backport. +public interface ICustomPotionParticles { + + /** + * Called from the event handler to spawn a single custom potion particle. To get an instance of + * Random inside this method, use world.rand. + * @param world The world to spawn the particle in. + * @param x The x coordinate of the particle, already set to a random value within the entity's bounding box. + * @param y The y coordinate of the particle, already set to a random value within the entity's bounding box. + * @param z The z coordinate of the particle, already set to a random value within the entity's bounding box. + */ + void spawnCustomParticle(World world, double x, double y, double z); + +} diff --git a/src/main/java/electroblob/wizardry/potion/PotionDecay.java b/src/main/java/electroblob/wizardry/potion/PotionDecay.java new file mode 100644 index 00000000..8386115a --- /dev/null +++ b/src/main/java/electroblob/wizardry/potion/PotionDecay.java @@ -0,0 +1,53 @@ +package electroblob.wizardry.potion; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.potion.Potion; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.DamageSource; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class PotionDecay extends Potion { + + private static final ResourceLocation ICON = new ResourceLocation(Wizardry.MODID, "textures/gui/decay_icon.png"); + + public PotionDecay(boolean isBadEffect, int liquidColour) { + super(isBadEffect, liquidColour); + // This needs to be here because registerPotionAttributeModifier doesn't like it if the potion has no name yet. + this.setPotionName("potion.wizardry:decay"); + this.registerPotionAttributeModifier(SharedMonsterAttributes.MOVEMENT_SPEED, "85602e0b-4801-4a87-94f3-bf617c97014e", -Constants.DECAY_SLOWNESS_PER_LEVEL, 2); + } + + @Override + public boolean isReady(int p_76397_1_, int p_76397_2_) { + // Copied from the vanilla wither effect. It does the timing stuff. 25 is the number of ticks between hits at + // amplifier 0 + int k = 25 >> p_76397_2_; + return k > 0 ? p_76397_1_ % k == 0 : true; + } + + @Override + public void performEffect(EntityLivingBase entitylivingbase, int strength) { + entitylivingbase.attackEntityFrom(DamageSource.wither, 1); + } + + @Override + @SideOnly(Side.CLIENT) + public void renderInventoryEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc) { + mc.renderEngine.bindTexture(ICON); + WizardryUtilities.drawTexturedRect(x + 6, y + 7, 0, 0, 18, 18, 18, 18); + } + + @Override + @SideOnly(Side.CLIENT) + public void renderHUDEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc, float alpha) { + mc.renderEngine.bindTexture(ICON); + WizardryUtilities.drawTexturedRect(x + 3, y + 3, 0, 0, 18, 18, 18, 18); + } + +} diff --git a/src/main/java/electroblob/wizardry/potion/PotionFrost.java b/src/main/java/electroblob/wizardry/potion/PotionFrost.java new file mode 100644 index 00000000..2b2c7783 --- /dev/null +++ b/src/main/java/electroblob/wizardry/potion/PotionFrost.java @@ -0,0 +1,53 @@ +package electroblob.wizardry.potion; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.potion.Potion; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class PotionFrost extends Potion implements ICustomPotionParticles { + + private static final ResourceLocation ICON = new ResourceLocation(Wizardry.MODID, "textures/gui/frost_icon.png"); + + public PotionFrost(boolean isBadEffect, int liquidColour) { + super(isBadEffect, liquidColour); + // This needs to be here because registerPotionAttributeModifier doesn't like it if the potion has no name yet. + this.setPotionName("potion.wizardry:frost"); + // With -0.5 as the 'amount', frost 1 slows the entity down by a half and frost 2 roots it to the spot + this.registerPotionAttributeModifier(SharedMonsterAttributes.MOVEMENT_SPEED, "35dded48-2f19-4541-8510-b29e2dc2cd51", -Constants.FROST_SLOWNESS_PER_LEVEL, 2); + // More UUIDs: 85602e0b-4801-4a87-94f3-bf617c97014e + } + + @Override + public void performEffect(EntityLivingBase entitylivingbase, int strength) { + // Nothing here because this potion works on attribute modifiers. + } + + @Override + public void spawnCustomParticle(World world, double x, double y, double z) { + Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, world, x, y, z, 0, -0.02, 0, 15 + world.rand.nextInt(5)); + } + + @Override + @SideOnly(Side.CLIENT) + public void renderInventoryEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc) { + mc.renderEngine.bindTexture(ICON); + WizardryUtilities.drawTexturedRect(x + 6, y + 7, 0, 0, 18, 18, 18, 18); + } + + @Override + @SideOnly(Side.CLIENT) + public void renderHUDEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc, float alpha) { + mc.renderEngine.bindTexture(ICON); + WizardryUtilities.drawTexturedRect(x + 3, y + 3, 0, 0, 18, 18, 18, 18); + } + +} diff --git a/src/main/java/electroblob/wizardry/potion/PotionMagicEffect.java b/src/main/java/electroblob/wizardry/potion/PotionMagicEffect.java new file mode 100644 index 00000000..bba9a646 --- /dev/null +++ b/src/main/java/electroblob/wizardry/potion/PotionMagicEffect.java @@ -0,0 +1,42 @@ +package electroblob.wizardry.potion; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.potion.Potion; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** Class for all potions that work on events only. */ +public class PotionMagicEffect extends Potion { + + private static final ResourceLocation ICONS = new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons.png"); + private final int textureIndex; + + public PotionMagicEffect(boolean isBadEffect, int liquidColour, int textureIndex) { + super(isBadEffect, liquidColour); + this.textureIndex = textureIndex; + } + + @Override + public void performEffect(EntityLivingBase entitylivingbase, int strength) { + // Nothing here because this potion works on events. + } + + @Override + @SideOnly(Side.CLIENT) + public void renderInventoryEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc){ + mc.renderEngine.bindTexture(ICONS); + WizardryUtilities.drawTexturedRect(x + 6, y + 7, 18*(textureIndex%4), 18*(textureIndex/4), 18, 18, 72, 72); + } + + @Override + @SideOnly(Side.CLIENT) + public void renderHUDEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc, float alpha) { + mc.renderEngine.bindTexture(ICONS); + WizardryUtilities.drawTexturedRect(x + 3, y + 3, 18*(textureIndex%4), 18*(textureIndex/4), 18, 18, 72, 72); + } + +} diff --git a/src/main/java/electroblob/wizardry/potion/PotionMagicEffectParticles.java b/src/main/java/electroblob/wizardry/potion/PotionMagicEffectParticles.java new file mode 100644 index 00000000..b9eac023 --- /dev/null +++ b/src/main/java/electroblob/wizardry/potion/PotionMagicEffectParticles.java @@ -0,0 +1,11 @@ +package electroblob.wizardry.potion; + +/** Same as {@link PotionMagicEffect}, but also implements {@link ICustomPotionParticles} to allow anonymous classes + * to extend it and add their own particles. */ +public abstract class PotionMagicEffectParticles extends PotionMagicEffect implements ICustomPotionParticles { + + public PotionMagicEffectParticles(boolean isBadEffect, int liquidColour, int textureIndex){ + super(isBadEffect, liquidColour, textureIndex); + } + +} diff --git a/src/main/java/electroblob/wizardry/registry/Spells.java b/src/main/java/electroblob/wizardry/registry/Spells.java new file mode 100644 index 00000000..f5205f29 --- /dev/null +++ b/src/main/java/electroblob/wizardry/registry/Spells.java @@ -0,0 +1,351 @@ +package electroblob.wizardry.registry; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.spell.*; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.registry.GameRegistry.ObjectHolder; +import net.minecraftforge.fml.common.registry.IForgeRegistry; +import net.minecraftforge.fml.common.registry.RegistryBuilder; + +/** + * Class responsible for defining, storing and registering all of wizardry's spells. + * @author Electroblob + * @since Wizardry 2.1 + */ +// In case anyone was wondering, the reason @ObjectHolder is useful within one mod is that it allows you to initialise +// stuff during the registry events (or whenever), whilst still having a final field (which is important, not only +// because it makes the text go bold, but also because it stops anyone fiddling with your fields). "Why would I want to +// initialise things within the registry events?", I hear you ask - well, for one, custom registries don't like it if +// you haven't created the registry before you start calling constructors of classes extending IForgeRegistryEntry.Impl. +@ObjectHolder(Wizardry.MODID) +@Mod.EventBusSubscriber +public final class Spells { + + // This is here because this class is already an event handler. + @SubscribeEvent + public static void createRegistry(RegistryEvent.NewRegistry event){ + + // Beats me why we need both of these. Surely the type parameter means it already knows? + RegistryBuilder builder = new RegistryBuilder(); + builder.setType(Spell.class); + builder.setName(new ResourceLocation(Wizardry.MODID, "spells")); + builder.setIDRange(0, 5000); // Is there any penalty for using a larger number? + + Spell.registry = builder.create(); + } + + // Wizardry 1.0 spells + + public static final Spell none = null; + public static final Spell magic_missile = null; + public static final Spell ignite = null; + public static final Spell freeze = null; + public static final Spell snowball = null; + public static final Spell arc = null; + public static final Spell thunderbolt = null; + public static final Spell summon_zombie = null; + public static final Spell snare = null; + public static final Spell dart = null; + public static final Spell light = null; + public static final Spell telekinesis = null; + public static final Spell heal = null; + + public static final Spell fireball = null; + public static final Spell flame_ray = null; + public static final Spell firebomb = null; + public static final Spell fire_sigil = null; + public static final Spell firebolt = null; + public static final Spell frost_ray = null; + public static final Spell summon_snow_golem = null; + public static final Spell ice_shard = null; + public static final Spell ice_statue = null; + public static final Spell frost_sigil = null; + public static final Spell lightning_ray = null; + public static final Spell spark_bomb = null; + public static final Spell homing_spark = null; + public static final Spell lightning_sigil = null; + public static final Spell lightning_arrow = null; + public static final Spell life_drain = null; + public static final Spell summon_skeleton = null; + public static final Spell metamorphosis = null; + public static final Spell wither = null; + public static final Spell poison = null; + public static final Spell growth_aura = null; + public static final Spell bubble = null; + public static final Spell whirlwind = null; + public static final Spell poison_bomb = null; + public static final Spell summon_spirit_wolf = null; + public static final Spell blink = null; + public static final Spell agility = null; + public static final Spell conjure_sword = null; + public static final Spell conjure_pickaxe = null; + public static final Spell conjure_bow = null; + public static final Spell force_arrow = null; + public static final Spell shield = null; + public static final Spell replenish_hunger = null; + public static final Spell cure_effects = null; + public static final Spell heal_ally = null; + + public static final Spell summon_blaze = null; + public static final Spell ring_of_fire = null; + public static final Spell detonate = null; + public static final Spell fire_resistance = null; + public static final Spell fireskin = null; + public static final Spell flaming_axe = null; + public static final Spell blizzard = null; + public static final Spell summon_ice_wraith = null; + public static final Spell ice_shroud = null; + public static final Spell ice_charge = null; + public static final Spell frost_axe = null; + public static final Spell invoke_weather = null; + public static final Spell chain_lightning = null; + public static final Spell lightning_bolt = null; + public static final Spell summon_lightning_wraith = null; + public static final Spell static_aura = null; + public static final Spell lightning_disc = null; + public static final Spell mind_control = null; + public static final Spell summon_wither_skeleton = null; + public static final Spell entrapment = null; + public static final Spell wither_skull = null; + public static final Spell darkness_orb = null; + public static final Spell shadow_ward = null; + public static final Spell decay = null; + public static final Spell water_breathing = null; + public static final Spell tornado = null; + public static final Spell glide = null; + public static final Spell summon_spirit_horse = null; + public static final Spell spider_swarm = null; + public static final Spell slime = null; + public static final Spell petrify = null; + public static final Spell invisibility = null; + public static final Spell levitation = null; + public static final Spell force_orb = null; + public static final Spell transportation = null; + public static final Spell spectral_pathway = null; + public static final Spell phase_step = null; + public static final Spell vanishing_box = null; + public static final Spell greater_heal = null; + public static final Spell healing_aura = null; + public static final Spell forcefield = null; + public static final Spell ironflesh = null; + public static final Spell transience = null; + + public static final Spell meteor = null; + public static final Spell firestorm = null; + public static final Spell summon_phoenix = null; + public static final Spell ice_age = null; + public static final Spell wall_of_frost = null; + public static final Spell summon_ice_giant = null; + public static final Spell thunderstorm = null; + public static final Spell lightning_hammer = null; + public static final Spell plague_of_darkness = null; + public static final Spell summon_skeleton_legion = null; + public static final Spell summon_shadow_wraith = null; + public static final Spell forests_curse = null; + public static final Spell flight = null; + public static final Spell silverfish_swarm = null; + public static final Spell black_hole = null; + public static final Spell shockwave = null; + public static final Spell summon_iron_golem = null; + public static final Spell arrow_rain = null; + public static final Spell diamondflesh = null; + public static final Spell font_of_vitality = null; + + // Wizardry 1.1 spells + + public static final Spell smoke_bomb = null; + public static final Spell mind_trick = null; + public static final Spell leap = null; + + public static final Spell pocket_furnace = null; + public static final Spell intimidate = null; + public static final Spell banish = null; + public static final Spell sixth_sense = null; + public static final Spell darkvision = null; + public static final Spell clairvoyance = null; + public static final Spell pocket_workbench = null; + public static final Spell imbue_weapon = null; + public static final Spell invigorating_presence = null; + public static final Spell oakflesh = null; + + public static final Spell greater_fireball = null; + public static final Spell flaming_weapon = null; + public static final Spell ice_lance = null; + public static final Spell freezing_weapon = null; + public static final Spell ice_spikes = null; + public static final Spell lightning_pulse = null; + public static final Spell curse_of_soulbinding = null; + public static final Spell cobwebs = null; + public static final Spell decoy = null; + public static final Spell arcane_jammer = null; + public static final Spell conjure_armour = null; + public static final Spell group_heal = null; + + public static final Spell hailstorm = null; + public static final Spell lightning_web = null; + public static final Spell summon_storm_elemental = null; + public static final Spell earthquake = null; + public static final Spell font_of_mana = null; + + @SubscribeEvent + public static void register(RegistryEvent.Register event){ + + IForgeRegistry registry = event.getRegistry(); + + // event.getRegistry should always equal Spell.registry. + registry.register(new None()); + registry.register(new MagicMissile()); + registry.register(new Ignite()); + registry.register(new Freeze()); + registry.register(new Snowball()); + registry.register(new Arc()); + registry.register(new Thunderbolt()); + registry.register(new SummonZombie()); + registry.register(new Snare()); + registry.register(new Dart()); + registry.register(new Light()); + registry.register(new Telekinesis()); + registry.register(new Heal()); + + registry.register(new Fireball()); + registry.register(new FlameRay()); + registry.register(new Firebomb()); + registry.register(new FireSigil()); + registry.register(new Firebolt()); + registry.register(new FrostRay()); + registry.register(new SummonSnowGolem()); + registry.register(new IceShard()); + registry.register(new IceStatue()); + registry.register(new FrostSigil()); + registry.register(new LightningRay()); + registry.register(new SparkBomb()); + registry.register(new HomingSpark()); + registry.register(new LightningSigil()); + registry.register(new LightningArrow()); + registry.register(new LifeDrain()); + registry.register(new SummonSkeleton()); + registry.register(new Metamorphosis()); + registry.register(new Wither()); + registry.register(new Poison()); + registry.register(new GrowthAura()); + registry.register(new Bubble()); + registry.register(new Whirlwind()); + registry.register(new PoisonBomb()); + registry.register(new SummonSpiritWolf()); + registry.register(new Blink()); + registry.register(new Agility()); + registry.register(new ConjureSword()); + registry.register(new ConjurePickaxe()); + registry.register(new ConjureBow()); + registry.register(new ForceArrow()); + registry.register(new Shield()); + registry.register(new ReplenishHunger()); + registry.register(new CureEffects()); + registry.register(new HealAlly()); + + registry.register(new SummonBlaze()); + registry.register(new RingOfFire()); + registry.register(new Detonate()); + registry.register(new FireResistance()); + registry.register(new Fireskin()); + registry.register(new FlamingAxe()); + registry.register(new Blizzard()); + registry.register(new SummonIceWraith()); + registry.register(new IceShroud()); + registry.register(new IceCharge()); + registry.register(new FrostAxe()); + registry.register(new InvokeWeather()); + registry.register(new ChainLightning()); + registry.register(new LightningBolt()); + registry.register(new SummonLightningWraith()); + registry.register(new StaticAura()); + registry.register(new LightningDisc()); + registry.register(new MindControl()); + registry.register(new SummonWitherSkeleton()); + registry.register(new Entrapment()); + registry.register(new WitherSkull()); + registry.register(new DarknessOrb()); + registry.register(new ShadowWard()); + registry.register(new Decay()); + registry.register(new WaterBreathing()); + registry.register(new Tornado()); + registry.register(new Glide()); + registry.register(new SummonSpiritHorse()); + registry.register(new SpiderSwarm()); + registry.register(new Slime()); + registry.register(new Petrify()); + registry.register(new Invisibility()); + registry.register(new Levitation()); + registry.register(new ForceOrb()); + registry.register(new Transportation()); + registry.register(new SpectralPathway()); + registry.register(new PhaseStep()); + registry.register(new VanishingBox()); + registry.register(new GreaterHeal()); + registry.register(new HealingAura()); + registry.register(new Forcefield()); + registry.register(new Ironflesh()); + registry.register(new Transience()); + + registry.register(new Meteor()); + registry.register(new Firestorm()); + registry.register(new SummonPhoenix()); + registry.register(new IceAge()); + registry.register(new WallOfFrost()); + registry.register(new SummonIceGiant()); + registry.register(new Thunderstorm()); + registry.register(new LightningHammer()); + registry.register(new PlagueOfDarkness()); + registry.register(new SummonSkeletonLegion()); + registry.register(new SummonShadowWraith()); + registry.register(new ForestsCurse()); + registry.register(new Flight()); + registry.register(new SilverfishSwarm()); + registry.register(new BlackHole()); + registry.register(new Shockwave()); + registry.register(new SummonIronGolem()); + registry.register(new ArrowRain()); + registry.register(new Diamondflesh()); + registry.register(new FontOfVitality()); + + // Wizardry 1.1 spells + + registry.register(new SmokeBomb()); + registry.register(new MindTrick()); + registry.register(new Leap()); + + registry.register(new PocketFurnace()); + registry.register(new Intimidate()); + registry.register(new Banish()); + registry.register(new SixthSense()); + registry.register(new Darkvision()); + registry.register(new Clairvoyance()); + registry.register(new PocketWorkbench()); + registry.register(new ImbueWeapon()); + registry.register(new InvigoratingPresence()); + registry.register(new Oakflesh()); + + registry.register(new GreaterFireball()); + registry.register(new FlamingWeapon()); + registry.register(new IceLance()); + registry.register(new FreezingWeapon()); + registry.register(new IceSpikes()); + registry.register(new LightningPulse()); + registry.register(new CurseOfSoulbinding()); + registry.register(new Cobwebs()); + registry.register(new Decoy()); + registry.register(new ArcaneJammer()); + registry.register(new ConjureArmour()); + registry.register(new GroupHeal()); + + registry.register(new Hailstorm()); + registry.register(new LightningWeb()); + registry.register(new SummonStormElemental()); + registry.register(new Earthquake()); + registry.register(new FontOfMana()); + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryAchievements.java b/src/main/java/electroblob/wizardry/registry/WizardryAchievements.java new file mode 100644 index 00000000..2e3ab165 --- /dev/null +++ b/src/main/java/electroblob/wizardry/registry/WizardryAchievements.java @@ -0,0 +1,47 @@ +package electroblob.wizardry.registry; + +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.stats.Achievement; +import net.minecraftforge.common.AchievementPage; + +/** + * Class responsible for defining, storing and registering all of wizardry's achievements (achievements don't have a + * registry, but it make sense to do them the same way as everything else). + * @author Electroblob + * @since Wizardry 2.1 + */ +public final class WizardryAchievements { + + public static final Achievement crystal = new Achievement("crystal", "crystal", -1, -1, WizardryItems.magic_crystal, null).registerStat(); + public static final Achievement arcane_initiate = new Achievement("arcane_initiate", "arcane_initiate", 2, -1, WizardryItems.magic_wand, crystal).registerStat(); + public static final Achievement apprentice = new Achievement("apprentice", "apprentice", 2, 3, WizardryItems.apprentice_wand, arcane_initiate).registerStat(); + public static final Achievement master = new Achievement("master", "master", 3, 6, WizardryItems.master_wand, apprentice).registerStat(); + public static final Achievement all_spells = new Achievement("all_spells", "all_spells", 5, 7, WizardryItems.wizard_handbook, master).registerStat().setSpecial(); + public static final Achievement wizard_trade = new Achievement("wizard_trade", "wizard_trade", 2, -5, Items.EMERALD, arcane_initiate).registerStat(); + public static final Achievement buy_master_spell = new Achievement("buy_master_spell", "buy_master_spell", 5, -5, WizardryItems.spell_book, wizard_trade).registerStat().setSpecial(); + public static final Achievement freeze_blaze = new Achievement("freeze_blaze", "freeze_blaze", -1, 3, Blocks.ICE, apprentice).registerStat(); + public static final Achievement charge_creeper = new Achievement("charge_creeper", "charge_creeper", -1, 1, Items.GUNPOWDER, arcane_initiate).registerStat(); + public static final Achievement frankenstein = new Achievement("frankenstein", "frankenstein", -3, 1, WizardryItems.advanced_lightning_wand, charge_creeper).registerStat().setSpecial(); + public static final Achievement special_upgrade = new Achievement("special_upgrade", "special_upgrade", 4, 1, WizardryItems.condenser_upgrade, arcane_initiate).registerStat(); + public static final Achievement craft_flask = new Achievement("craft_flask", "craft_flask", 4, -2, WizardryItems.mana_flask, arcane_initiate).registerStat(); + public static final Achievement elemental = new Achievement("elemental", "elemental", 6, -1, WizardryItems.basic_fire_wand, arcane_initiate).registerStat(); + public static final Achievement armour_set = new Achievement("armour_set", "armour_set", 0, -4, WizardryItems.wizard_hat, arcane_initiate).registerStat(); + public static final Achievement legendary = new Achievement("legendary", "legendary", -2, -5, WizardryItems.armour_upgrade, armour_set).registerStat().setSpecial(); + public static final Achievement self_destruct = new Achievement("self_destruct", "self_destruct", 1, 0, Blocks.PUMPKIN, arcane_initiate).registerStat(); + public static final Achievement pig_tornado = new Achievement("pig_tornado", "pig_tornado", 6, 3, Items.SADDLE, apprentice).registerStat().setSpecial(); + public static final Achievement jam_wizard = new Achievement("jam_wizard", "jam_wizard", 4, 4, Blocks.WEB, apprentice).registerStat(); + public static final Achievement slime_skeleton = new Achievement("slime_skeleton", "slime_skeleton", 1, 5, Items.SLIME_BALL, apprentice).registerStat(); + public static final Achievement anger_wizard = new Achievement("anger_wizard", "anger_wizard", 1, -7, Items.IRON_SWORD, wizard_trade).registerStat(); + public static final Achievement defeat_evil_wizard = new Achievement("defeat_evil_wizard", "defeat_evil_wizard", 4, -7, WizardryItems.wizard_boots_necromancy, wizard_trade).registerStat(); + public static final Achievement max_out_wand = new Achievement("max_out_wand", "max_out_wand", 7, 1, WizardryItems.arcane_tome, special_upgrade).registerStat().setSpecial(); + public static final Achievement element_master = new Achievement("element_master", "element_master", 7, -3, WizardryItems.master_ice_wand, elemental).registerStat().setSpecial(); + public static final Achievement identify_spell = new Achievement("identify_spell", "identify_spell", -2, -3, WizardryItems.identification_scroll, arcane_initiate).registerStat(); + + private static final Achievement[] ACHIEVEMENTS_LIST = {crystal, arcane_initiate, apprentice, master, all_spells, wizard_trade, buy_master_spell, freeze_blaze, charge_creeper, + frankenstein, special_upgrade, craft_flask, elemental, armour_set, legendary, self_destruct, pig_tornado, jam_wizard, slime_skeleton, anger_wizard, defeat_evil_wizard, + max_out_wand, element_master, identify_spell}; + + public static final AchievementPage WIZARDRY_ACHIEVEMENT_PAGE = new AchievementPage("Wizardry", ACHIEVEMENTS_LIST); + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java new file mode 100644 index 00000000..b1efa21a --- /dev/null +++ b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java @@ -0,0 +1,92 @@ +package electroblob.wizardry.registry; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.block.BlockArcaneWorkbench; +import electroblob.wizardry.block.BlockCrystalFlower; +import electroblob.wizardry.block.BlockCrystalOre; +import electroblob.wizardry.block.BlockMagicLight; +import electroblob.wizardry.block.BlockSnare; +import electroblob.wizardry.block.BlockSpectral; +import electroblob.wizardry.block.BlockStatue; +import electroblob.wizardry.block.BlockTransportationStone; +import electroblob.wizardry.block.BlockVanishingCobweb; +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.registry.IForgeRegistry; + +/** + * Class responsible for defining, storing and registering all of wizardry's blocks. + * @author Electroblob + * @since Wizardry 2.1 + */ +@Mod.EventBusSubscriber +public final class WizardryBlocks { + + // I get registry events, they make sense - even I doubted myself with when to do various things in the load process, + // so I can see why the folks at Forge wanted to save us having to think about it. + // What I do not understand is why you would use @ObjectHolder for your own blocks and items. What's the point in + // making your code longer and more complicated, when you can just define the blocks as constants and register them + // later? + + // Found a very nice way of registering things using arrays, which might make @ObjectHolder actually useful. + // http://www.minecraftforge.net/forum/topic/49497-1112-is-using-registryevent-this-way-ok/ + + // Anything set to use the material 'air' will not render, even with a TESR! + + // setSoundType should be public, but in this particular version it isn't... which is a bit of a pain. + + public static final Block arcane_workbench = new BlockArcaneWorkbench().setHardness(1.0F).setCreativeTab(WizardryTabs.WIZARDRY); + public static final Block crystal_ore = new BlockCrystalOre(Material.ROCK).setHardness(3.0F).setCreativeTab(WizardryTabs.WIZARDRY); + public static final Block petrified_stone = new BlockStatue(Material.ROCK).setHardness(1.5F).setResistance(10.0F); + public static final Block ice_statue = new BlockStatue(Material.ICE).setHardness(0.5F).setLightOpacity(3); + public static final Block magic_light = new BlockMagicLight(Material.CIRCUITS); + public static final Block crystal_flower = new BlockCrystalFlower(Material.PLANTS).setHardness(0.0F).setCreativeTab(WizardryTabs.WIZARDRY); + public static final Block snare = new BlockSnare(Material.PLANTS).setHardness(0.0F); + public static final Block transportation_stone = new BlockTransportationStone(Material.ROCK).setHardness(0.0F).setLightLevel(0.5f).setLightOpacity(0).setCreativeTab(WizardryTabs.WIZARDRY); + public static final Block spectral_block = new BlockSpectral(Material.GLASS).setLightLevel(0.7f).setLightOpacity(0).setBlockUnbreakable().setResistance(6000000.0F); + public static final Block crystal_block = new Block(Material.IRON).setHardness(5.0F).setResistance(10.0F).setCreativeTab(WizardryTabs.WIZARDRY); + public static final Block meteor = new Block(Material.ROCK).setLightLevel(1); + public static final Block vanishing_cobweb = new BlockVanishingCobweb(Material.WEB).setLightOpacity(1).setHardness(4.0F); + + static { + // This is here because Block#setHarvestLevel isn't chainable. + crystal_block.setHarvestLevel("pickaxe", 2); + } + + /** + * Sets both the registry and unlocalised names of the given block, then registers it with the given registry. Use + * this instead of {@link Block#setRegistryName(String)} and {@link Block#setUnlocalizedName(String)} during + * construction, for convenience and consistency. + * @param registry The registry to register the given block to. + * @param item The block to register. + * @param name The name of the block, without the mod ID or the .name stuff. The registry name will be + * {@code wizardry:[name]}. The unlocalised name will be {@code tile.wizardry:[name].name}. + */ + public static void registerBlock(IForgeRegistry registry, Block block, String name){ + block.setRegistryName(Wizardry.MODID, name); + block.setUnlocalizedName(block.getRegistryName().toString()); + registry.register(block); + } + + @SubscribeEvent + public static void register(RegistryEvent.Register event){ + + IForgeRegistry registry = event.getRegistry(); + + registerBlock(registry, arcane_workbench, "arcane_workbench"); + registerBlock(registry, crystal_ore, "crystal_ore"); + registerBlock(registry, petrified_stone, "petrified_stone"); + registerBlock(registry, ice_statue, "ice_statue"); + registerBlock(registry, magic_light, "magic_light"); + registerBlock(registry, crystal_flower, "crystal_flower"); + registerBlock(registry, snare, "snare"); + registerBlock(registry, transportation_stone, "transportation_stone"); + registerBlock(registry, spectral_block, "spectral_block"); + registerBlock(registry, crystal_block, "crystal_block"); + registerBlock(registry, meteor, "meteor"); + registerBlock(registry, vanishing_cobweb, "vanishing_cobweb"); + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryEnchantments.java b/src/main/java/electroblob/wizardry/registry/WizardryEnchantments.java new file mode 100644 index 00000000..e34ca427 --- /dev/null +++ b/src/main/java/electroblob/wizardry/registry/WizardryEnchantments.java @@ -0,0 +1,41 @@ +package electroblob.wizardry.registry; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.enchantment.EnchantmentMagicSword; +import electroblob.wizardry.enchantment.EnchantmentTimed; +import net.minecraft.enchantment.Enchantment; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +/** + * Class responsible for defining, storing and registering all of wizardry's enchantments. + * @author Electroblob + * @since Wizardry 2.1 + */ +@Mod.EventBusSubscriber +public final class WizardryEnchantments { + + // At the moment these enchantments generate on books in dungeon chests due to a bad bit of code (EnchantRandomly:50). + // No idea how to fix this because I have no way of hooking into that code... removing the enchantments from the + // registry works, but breaks everything else! + + // TODO: For the time being, a dynamic solution will have to do, i.e. intercept the book when it is generated and + // reassign its enchantment. + + // All of these have custom classes, so the unlocalised name (referred to simply as 'name' for enchantments) is + // dealt with inside those classes. + public static final Enchantment magic_sword = new EnchantmentMagicSword().setRegistryName(Wizardry.MODID, "magic_sword"); + public static final Enchantment magic_bow = new EnchantmentTimed().setRegistryName(Wizardry.MODID, "magic_bow"); + public static final Enchantment flaming_weapon = new EnchantmentTimed().setRegistryName(Wizardry.MODID, "flaming_weapon"); + public static final Enchantment freezing_weapon = new EnchantmentTimed().setRegistryName(Wizardry.MODID, "freezing_weapon"); + + @SubscribeEvent + public static void register(RegistryEvent.Register event){ + event.getRegistry().register(magic_sword); + event.getRegistry().register(magic_bow); + event.getRegistry().register(flaming_weapon); + event.getRegistry().register(freezing_weapon); + } + +} diff --git a/src/main/java/electroblob/wizardry/registry/WizardryItems.java b/src/main/java/electroblob/wizardry/registry/WizardryItems.java new file mode 100644 index 00000000..89b053dd --- /dev/null +++ b/src/main/java/electroblob/wizardry/registry/WizardryItems.java @@ -0,0 +1,419 @@ +package electroblob.wizardry.registry; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.item.ItemArcaneTome; +import electroblob.wizardry.item.ItemArmourUpgrade; +import electroblob.wizardry.item.ItemFirebomb; +import electroblob.wizardry.item.ItemFlamingAxe; +import electroblob.wizardry.item.ItemFrostAxe; +import electroblob.wizardry.item.ItemIdentificationScroll; +import electroblob.wizardry.item.ItemPoisonBomb; +import electroblob.wizardry.item.ItemScroll; +import electroblob.wizardry.item.ItemSmokeBomb; +import electroblob.wizardry.item.ItemSpectralArmour; +import electroblob.wizardry.item.ItemSpectralBow; +import electroblob.wizardry.item.ItemSpectralPickaxe; +import electroblob.wizardry.item.ItemSpectralSword; +import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.item.ItemWizardArmour; +import electroblob.wizardry.item.ItemWizardHandbook; +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.Item; +import net.minecraft.item.Item.ToolMaterial; +import net.minecraft.item.ItemArmor.ArmorMaterial; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.common.util.EnumHelper; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.registry.IForgeRegistry; + +/** + * Class responsible for defining, storing and registering all of wizardry's items. Also registers the ItemBlocks for + * wizardry's blocks. + * @author Electroblob + * @since Wizardry 2.1 + */ +@Mod.EventBusSubscriber +public final class WizardryItems { + + public static final Item magic_crystal = new Item().setCreativeTab(WizardryTabs.WIZARDRY); + + public static final Item magic_wand = new ItemWand(Tier.BASIC, null); + public static final Item apprentice_wand = new ItemWand(Tier.APPRENTICE, null); + public static final Item advanced_wand = new ItemWand(Tier.ADVANCED, null); + public static final Item master_wand = new ItemWand(Tier.MASTER, null); + + public static final Item arcane_tome = new ItemArcaneTome(); + public static final Item wizard_handbook = new ItemWizardHandbook(); + public static final Item spell_book = new ItemSpellBook(); + + public static final Item basic_fire_wand = new ItemWand(Tier.BASIC, Element.FIRE); + public static final Item basic_ice_wand = new ItemWand(Tier.BASIC, Element.ICE); + public static final Item basic_lightning_wand = new ItemWand(Tier.BASIC, Element.LIGHTNING); + public static final Item basic_necromancy_wand = new ItemWand(Tier.BASIC, Element.NECROMANCY); + public static final Item basic_earth_wand = new ItemWand(Tier.BASIC, Element.EARTH); + public static final Item basic_sorcery_wand = new ItemWand(Tier.BASIC, Element.SORCERY); + public static final Item basic_healing_wand = new ItemWand(Tier.BASIC, Element.HEALING); + + public static final Item apprentice_fire_wand = new ItemWand(Tier.APPRENTICE, Element.FIRE); + public static final Item apprentice_ice_wand = new ItemWand(Tier.APPRENTICE, Element.ICE); + public static final Item apprentice_lightning_wand = new ItemWand(Tier.APPRENTICE, Element.LIGHTNING); + public static final Item apprentice_necromancy_wand = new ItemWand(Tier.APPRENTICE, Element.NECROMANCY); + public static final Item apprentice_earth_wand = new ItemWand(Tier.APPRENTICE, Element.EARTH); + public static final Item apprentice_sorcery_wand = new ItemWand(Tier.APPRENTICE, Element.SORCERY); + public static final Item apprentice_healing_wand = new ItemWand(Tier.APPRENTICE, Element.HEALING); + + public static final Item advanced_fire_wand = new ItemWand(Tier.ADVANCED, Element.FIRE); + public static final Item advanced_ice_wand = new ItemWand(Tier.ADVANCED, Element.ICE); + public static final Item advanced_lightning_wand = new ItemWand(Tier.ADVANCED, Element.LIGHTNING); + public static final Item advanced_necromancy_wand = new ItemWand(Tier.ADVANCED, Element.NECROMANCY); + public static final Item advanced_earth_wand = new ItemWand(Tier.ADVANCED, Element.EARTH); + public static final Item advanced_sorcery_wand = new ItemWand(Tier.ADVANCED, Element.SORCERY); + public static final Item advanced_healing_wand = new ItemWand(Tier.ADVANCED, Element.HEALING); + + public static final Item master_fire_wand = new ItemWand(Tier.MASTER, Element.FIRE); + public static final Item master_ice_wand = new ItemWand(Tier.MASTER, Element.ICE); + public static final Item master_lightning_wand = new ItemWand(Tier.MASTER, Element.LIGHTNING); + public static final Item master_necromancy_wand = new ItemWand(Tier.MASTER, Element.NECROMANCY); + public static final Item master_earth_wand = new ItemWand(Tier.MASTER, Element.EARTH); + public static final Item master_sorcery_wand = new ItemWand(Tier.MASTER, Element.SORCERY); + public static final Item master_healing_wand = new ItemWand(Tier.MASTER, Element.HEALING); + + public static final Item spectral_sword = new ItemSpectralSword(ToolMaterial.IRON); + public static final Item spectral_pickaxe = new ItemSpectralPickaxe(ToolMaterial.IRON); + public static final Item spectral_bow = new ItemSpectralBow(); + + public static final Item mana_flask = new Item(){ + @Override + public void onCreated(ItemStack par1ItemStack, World par2World, EntityPlayer entityplayer){ + entityplayer.addStat(WizardryAchievements.craft_flask); + } + }.setCreativeTab(WizardryTabs.WIZARDRY); + + public static final Item storage_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); + public static final Item siphon_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); + public static final Item condenser_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); + public static final Item range_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); + public static final Item duration_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); + public static final Item cooldown_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); + public static final Item blast_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); + public static final Item attunement_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); + + public static final Item magic_silk = new Item().setCreativeTab(WizardryTabs.WIZARDRY); + + public static final Item.ToolMaterial MAGICAL = EnumHelper.addToolMaterial("MAGICAL", 3, 1000, 8.0f, 4.0f, 0); + + public static final Item flaming_axe = new ItemFlamingAxe(MAGICAL); + public static final Item frost_axe = new ItemFrostAxe(MAGICAL); + + public static final Item firebomb = new ItemFirebomb(); + public static final Item poison_bomb = new ItemPoisonBomb(); + + public static final Item blank_scroll = new Item().setCreativeTab(WizardryTabs.WIZARDRY); + public static final Item scroll = new ItemScroll().setCreativeTab(WizardryTabs.SPELLS); + + // The only way to get these is in dungeon chests (They are legendary, after all. Wizards don't just have them. + // Also if they were sold you could buy four from the same wizard - and that's no fun at all!) + public static final Item armour_upgrade = new ItemArmourUpgrade(); + + public static final ArmorMaterial SILK = EnumHelper.addArmorMaterial("SILK", "wizardry/textures/armour/wizard_armour", + 15, new int[]{2, 5, 4, 2}, 0, SoundEvents.ITEM_ARMOR_EQUIP_LEATHER, 0.0F); + // Saw a post somewhere that said you have to put these in the init methods rather than defining them as constants. + // I *think* that's because the post was for a newer Minecraft version, where custom armour has its own renderer or + // something, meaning it would be done a lot like the entity rendering registry in ClientProxy. This is all working + // fine it seems, so I'm not going to fiddle with it, but it might be useful to know if I update versions again. + public static final Item wizard_hat = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, null); + public static final Item wizard_robe = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, null); + public static final Item wizard_leggings = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, null); + public static final Item wizard_boots = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, null); + + public static final Item wizard_hat_fire = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, Element.FIRE); + public static final Item wizard_robe_fire = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, Element.FIRE); + public static final Item wizard_leggings_fire = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, Element.FIRE); + public static final Item wizard_boots_fire = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, Element.FIRE); + + public static final Item wizard_hat_ice = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, Element.ICE); + public static final Item wizard_robe_ice = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, Element.ICE); + public static final Item wizard_leggings_ice = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, Element.ICE); + public static final Item wizard_boots_ice = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, Element.ICE); + + public static final Item wizard_hat_lightning = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, Element.LIGHTNING); + public static final Item wizard_robe_lightning = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, Element.LIGHTNING); + public static final Item wizard_leggings_lightning = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, Element.LIGHTNING); + public static final Item wizard_boots_lightning = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, Element.LIGHTNING); + + public static final Item wizard_hat_necromancy = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, Element.NECROMANCY); + public static final Item wizard_robe_necromancy = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, Element.NECROMANCY); + public static final Item wizard_leggings_necromancy = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, Element.NECROMANCY); + public static final Item wizard_boots_necromancy = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, Element.NECROMANCY); + + public static final Item wizard_hat_earth = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, Element.EARTH); + public static final Item wizard_robe_earth = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, Element.EARTH); + public static final Item wizard_leggings_earth = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, Element.EARTH); + public static final Item wizard_boots_earth = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, Element.EARTH); + + public static final Item wizard_hat_sorcery = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, Element.SORCERY); + public static final Item wizard_robe_sorcery = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, Element.SORCERY); + public static final Item wizard_leggings_sorcery = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, Element.SORCERY); + public static final Item wizard_boots_sorcery = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, Element.SORCERY); + + public static final Item wizard_hat_healing = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, Element.HEALING); + public static final Item wizard_robe_healing = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, Element.HEALING); + public static final Item wizard_leggings_healing = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, Element.HEALING); + public static final Item wizard_boots_healing = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, Element.HEALING); + + public static final Item spectral_helmet = new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.HEAD); + public static final Item spectral_chestplate = new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.CHEST); + public static final Item spectral_leggings = new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.LEGS); + public static final Item spectral_boots = new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.FEET); + + public static final Item smoke_bomb = new ItemSmokeBomb(); + + public static final Item identification_scroll = new ItemIdentificationScroll(); + + public static final Map, Item> WAND_MAP = new HashMap<>(); + public static final Map, Item> ARMOUR_MAP = new HashMap<>(); + + static { + + WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.MAGIC), magic_wand); + WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.FIRE), basic_fire_wand); + WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.ICE), basic_ice_wand); + WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.LIGHTNING), basic_lightning_wand); + WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.NECROMANCY), basic_necromancy_wand); + WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.EARTH), basic_earth_wand); + WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.SORCERY), basic_sorcery_wand); + WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.HEALING), basic_healing_wand); + WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.MAGIC), apprentice_wand); + WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.FIRE), apprentice_fire_wand); + WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.ICE), apprentice_ice_wand); + WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.LIGHTNING), apprentice_lightning_wand); + WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.NECROMANCY), apprentice_necromancy_wand); + WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.EARTH), apprentice_earth_wand); + WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.SORCERY), apprentice_sorcery_wand); + WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.HEALING), apprentice_healing_wand); + WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.MAGIC), advanced_wand); + WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.FIRE), advanced_fire_wand); + WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.ICE), advanced_ice_wand); + WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.LIGHTNING), advanced_lightning_wand); + WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.NECROMANCY), advanced_necromancy_wand); + WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.EARTH), advanced_earth_wand); + WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.SORCERY), advanced_sorcery_wand); + WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.HEALING), advanced_healing_wand); + WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.MAGIC), master_wand); + WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.FIRE), master_fire_wand); + WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.ICE), master_ice_wand); + WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.LIGHTNING), master_lightning_wand); + WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.NECROMANCY), master_necromancy_wand); + WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.EARTH), master_earth_wand); + WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.SORCERY), master_sorcery_wand); + WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.HEALING), master_healing_wand); + + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.MAGIC), wizard_hat); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.FIRE), wizard_hat_fire); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.ICE), wizard_hat_ice); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.LIGHTNING), wizard_hat_lightning); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.NECROMANCY), wizard_hat_necromancy); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.EARTH), wizard_hat_earth); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.SORCERY), wizard_hat_sorcery); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.HEALING), wizard_hat_healing); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.MAGIC), wizard_robe); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.FIRE), wizard_robe_fire); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.ICE), wizard_robe_ice); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.LIGHTNING), wizard_robe_lightning); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.NECROMANCY), wizard_robe_necromancy); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.EARTH), wizard_robe_earth); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.SORCERY), wizard_robe_sorcery); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.HEALING), wizard_robe_healing); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.MAGIC), wizard_leggings); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.FIRE), wizard_leggings_fire); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.ICE), wizard_leggings_ice); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.LIGHTNING), wizard_leggings_lightning); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.NECROMANCY), wizard_leggings_necromancy); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.EARTH), wizard_leggings_earth); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.SORCERY), wizard_leggings_sorcery); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.HEALING), wizard_leggings_healing); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.MAGIC), wizard_boots); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.FIRE), wizard_boots_fire); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.ICE), wizard_boots_ice); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.LIGHTNING), wizard_boots_lightning); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.NECROMANCY), wizard_boots_necromancy); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.EARTH), wizard_boots_earth); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.SORCERY), wizard_boots_sorcery); + ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.HEALING), wizard_boots_healing); + } + + /** + * Sets both the registry and unlocalised names of the given item, then registers it with the given registry. Use + * this instead of {@link Item#setRegistryName(String)} and {@link Item#setUnlocalizedName(String)} during + * construction, for convenience and consistency. + * @param registry The registry to register the given item to. + * @param item The item to register. + * @param name The name of the item, without the mod ID or the .name stuff. The registry name will be + * {@code wizardry:[name]}. The unlocalised name will be {@code item.wizardry:[name].name}. + */ + public static void registerItem(IForgeRegistry registry, Item item, String name){ + item.setRegistryName(Wizardry.MODID, name); + item.setUnlocalizedName(item.getRegistryName().toString()); + registry.register(item); + } + + /** Registers an ItemBlock afor the given block, with the same registry name as that block. */ + private static void registerItemBlock(IForgeRegistry registry, Block block){ + // We don't need to keep a reference to the ItemBlock, so this can all be done in one line. + registry.register(new ItemBlock(block).setRegistryName(block.getRegistryName())); + } + + @SubscribeEvent + public static void register(RegistryEvent.Register event){ + + IForgeRegistry registry = event.getRegistry(); + + // ItemBlocks + + // Not all blocks need an ItemBlock + registerItemBlock(registry, WizardryBlocks.arcane_workbench); + registerItemBlock(registry, WizardryBlocks.crystal_ore); + registerItemBlock(registry, WizardryBlocks.crystal_flower); + registerItemBlock(registry, WizardryBlocks.transportation_stone); + registerItemBlock(registry, WizardryBlocks.crystal_block); + + // Items + + registerItem(registry, magic_crystal, "magic_crystal"); + + registerItem(registry, magic_wand, "magic_wand"); + registerItem(registry, apprentice_wand, "apprentice_wand"); + registerItem(registry, advanced_wand, "advanced_wand"); + registerItem(registry, master_wand, "master_wand"); + + registerItem(registry, spell_book, "spell_book"); + registerItem(registry, arcane_tome, "arcane_tome"); + registerItem(registry, wizard_handbook, "wizard_handbook"); + + registerItem(registry, basic_fire_wand, "basic_fire_wand"); + registerItem(registry, basic_ice_wand, "basic_ice_wand"); + registerItem(registry, basic_lightning_wand, "basic_lightning_wand"); + registerItem(registry, basic_necromancy_wand, "basic_necromancy_wand"); + registerItem(registry, basic_earth_wand, "basic_earth_wand"); + registerItem(registry, basic_sorcery_wand, "basic_sorcery_wand"); + registerItem(registry, basic_healing_wand, "basic_healing_wand"); + + registerItem(registry, apprentice_fire_wand, "apprentice_fire_wand"); + registerItem(registry, apprentice_ice_wand, "apprentice_ice_wand"); + registerItem(registry, apprentice_lightning_wand, "apprentice_lightning_wand"); + registerItem(registry, apprentice_necromancy_wand, "apprentice_necromancy_wand"); + registerItem(registry, apprentice_earth_wand, "apprentice_earth_wand"); + registerItem(registry, apprentice_sorcery_wand, "apprentice_sorcery_wand"); + registerItem(registry, apprentice_healing_wand, "apprentice_healing_wand"); + + registerItem(registry, advanced_fire_wand, "advanced_fire_wand"); + registerItem(registry, advanced_ice_wand, "advanced_ice_wand"); + registerItem(registry, advanced_lightning_wand, "advanced_lightning_wand"); + registerItem(registry, advanced_necromancy_wand, "advanced_necromancy_wand"); + registerItem(registry, advanced_earth_wand, "advanced_earth_wand"); + registerItem(registry, advanced_sorcery_wand, "advanced_sorcery_wand"); + registerItem(registry, advanced_healing_wand, "advanced_healing_wand"); + + registerItem(registry, master_fire_wand, "master_fire_wand"); + registerItem(registry, master_ice_wand, "master_ice_wand"); + registerItem(registry, master_lightning_wand, "master_lightning_wand"); + registerItem(registry, master_necromancy_wand, "master_necromancy_wand"); + registerItem(registry, master_earth_wand, "master_earth_wand"); + registerItem(registry, master_sorcery_wand, "master_sorcery_wand"); + registerItem(registry, master_healing_wand, "master_healing_wand"); + + registerItem(registry, spectral_sword, "spectral_sword"); + registerItem(registry, spectral_pickaxe, "spectral_pickaxe"); + registerItem(registry, spectral_bow, "spectral_bow"); + + registerItem(registry, mana_flask, "mana_flask"); + + registerItem(registry, storage_upgrade, "storage_upgrade"); + registerItem(registry, siphon_upgrade, "siphon_upgrade"); + registerItem(registry, condenser_upgrade, "condenser_upgrade"); + registerItem(registry, range_upgrade, "range_upgrade"); + registerItem(registry, duration_upgrade, "duration_upgrade"); + registerItem(registry, cooldown_upgrade, "cooldown_upgrade"); + registerItem(registry, blast_upgrade, "blast_upgrade"); + registerItem(registry, attunement_upgrade, "attunement_upgrade"); + + registerItem(registry, flaming_axe, "flaming_axe"); + registerItem(registry, frost_axe, "frost_axe"); + + registerItem(registry, firebomb, "firebomb"); + registerItem(registry, poison_bomb, "poison_bomb"); + + registerItem(registry, blank_scroll, "blank_scroll"); + registerItem(registry, scroll, "scroll"); + + registerItem(registry, armour_upgrade, "armour_upgrade"); + + registerItem(registry, magic_silk, "magic_silk"); + + registerItem(registry, wizard_hat, "wizard_hat"); + registerItem(registry, wizard_robe, "wizard_robe"); + registerItem(registry, wizard_leggings, "wizard_leggings"); + registerItem(registry, wizard_boots, "wizard_boots"); + + registerItem(registry, wizard_hat_fire, "wizard_hat_fire"); + registerItem(registry, wizard_robe_fire, "wizard_robe_fire"); + registerItem(registry, wizard_leggings_fire, "wizard_leggings_fire"); + registerItem(registry, wizard_boots_fire, "wizard_boots_fire"); + + registerItem(registry, wizard_hat_ice, "wizard_hat_ice"); + registerItem(registry, wizard_robe_ice, "wizard_robe_ice"); + registerItem(registry, wizard_leggings_ice, "wizard_leggings_ice"); + registerItem(registry, wizard_boots_ice, "wizard_boots_ice"); + + registerItem(registry, wizard_hat_lightning, "wizard_hat_lightning"); + registerItem(registry, wizard_robe_lightning, "wizard_robe_lightning"); + registerItem(registry, wizard_leggings_lightning, "wizard_leggings_lightning"); + registerItem(registry, wizard_boots_lightning, "wizard_boots_lightning"); + + registerItem(registry, wizard_hat_necromancy, "wizard_hat_necromancy"); + registerItem(registry, wizard_robe_necromancy, "wizard_robe_necromancy"); + registerItem(registry, wizard_leggings_necromancy, "wizard_leggings_necromancy"); + registerItem(registry, wizard_boots_necromancy, "wizard_boots_necromancy"); + + registerItem(registry, wizard_hat_earth, "wizard_hat_earth"); + registerItem(registry, wizard_robe_earth, "wizard_robe_earth"); + registerItem(registry, wizard_leggings_earth, "wizard_leggings_earth"); + registerItem(registry, wizard_boots_earth, "wizard_boots_earth"); + + registerItem(registry, wizard_hat_sorcery, "wizard_hat_sorcery"); + registerItem(registry, wizard_robe_sorcery, "wizard_robe_sorcery"); + registerItem(registry, wizard_leggings_sorcery, "wizard_leggings_sorcery"); + registerItem(registry, wizard_boots_sorcery, "wizard_boots_sorcery"); + + registerItem(registry, wizard_hat_healing, "wizard_hat_healing"); + registerItem(registry, wizard_robe_healing, "wizard_robe_healing"); + registerItem(registry, wizard_leggings_healing, "wizard_leggings_healing"); + registerItem(registry, wizard_boots_healing, "wizard_boots_healing"); + + registerItem(registry, spectral_helmet, "spectral_helmet"); + registerItem(registry, spectral_chestplate, "spectral_chestplate"); + registerItem(registry, spectral_leggings, "spectral_leggings"); + registerItem(registry, spectral_boots, "spectral_boots"); + + registerItem(registry, smoke_bomb, "smoke_bomb"); + + registerItem(registry, identification_scroll, "identification_scroll"); + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryPotions.java b/src/main/java/electroblob/wizardry/registry/WizardryPotions.java new file mode 100644 index 00000000..4b51a675 --- /dev/null +++ b/src/main/java/electroblob/wizardry/registry/WizardryPotions.java @@ -0,0 +1,113 @@ +package electroblob.wizardry.registry; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.potion.PotionDecay; +import electroblob.wizardry.potion.PotionFrost; +import electroblob.wizardry.potion.PotionMagicEffect; +import electroblob.wizardry.potion.PotionMagicEffectParticles; +import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.potion.Potion; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.world.World; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.registry.IForgeRegistry; + +/** + * Class responsible for defining, storing and registering all of wizardry's potion effects. + * @author Electroblob + * @since Wizardry 2.1 + */ +@Mod.EventBusSubscriber +public final class WizardryPotions { + + /* Interestingly, setting the colour to black stops the particles from rendering. This is great, however, the black + * colour then 'mixes' with other potions that are applied. Whilst this is a bit annoying, for the amount it is + * likely to get noticed it is certainly preferable to always making showParticles false, because now I can give + * the user the option (via commands) of having one of my potion effects without particles, and more importantly + * the potion effect HUD still gets displayed. TODO: Backport to 1.7.10, assuming it also works in that version. + * This means also changing whenever the potion effect is added such that it is NOT ambient. */ + + public static final Potion frost = new PotionFrost(true, 0); // Colour was 0x38ddec (was arbitrary anyway) + + public static final Potion transience = new PotionMagicEffectParticles(false, 0, 0){ + @Override + public void spawnCustomParticle(World world, double x, double y, double z){ + Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, world, x, y, z, 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)), 0.8f, 0.8f, 1.0f); + } + }.setBeneficial(); //0xffe89b + + public static final Potion fireskin = new PotionMagicEffectParticles(false, 0, 1){ + @Override + public void spawnCustomParticle(World world, double x, double y, double z){ + world.spawnParticle(EnumParticleTypes.FLAME, x, y, z, 0, 0, 0); + } + @Override + public void performEffect(EntityLivingBase entitylivingbase, int strength){ + entitylivingbase.extinguish(); // Stops melee mobs that are on fire from setting the player on fire, + // without allowing the player to actually stand in fire or swim in lava without taking damage. + }; + }.setBeneficial(); //0xff2f02 + + public static final Potion ice_shroud = new PotionMagicEffectParticles(false, 0, 2){ + @Override + public void spawnCustomParticle(World world, double x, double y, double z){ + float brightness = 0.5f + (world.rand.nextFloat()/2); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x, y, z, 0, 0, 0, 48 + world.rand.nextInt(12), brightness, brightness + 0.1f, 1.0f, true, 0); + Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, world, x, y, z, 0, -0.02, 0, 40 + world.rand.nextInt(10)); + } + }.setBeneficial(); //0x52f1ff + + public static final Potion static_aura = new PotionMagicEffectParticles(false, 0, 3){ + @Override + public void spawnCustomParticle(World world, double x, double y, double z){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, x, y, z, 0, 0, 0, 3); + } + }.setBeneficial(); //0x0070ff + + public static final Potion decay = new PotionDecay(true, 0x3c006c); + public static final Potion sixth_sense = new PotionMagicEffect(false, 0xc6ff01, 4).setBeneficial(); + public static final Potion arcane_jammer = new PotionMagicEffect(false, 0xcf4aa2, 5); + public static final Potion mind_trick = new PotionMagicEffect(true, 0x601683, 6); + public static final Potion mind_control = new PotionMagicEffect(true, 0x320b44, 7); + public static final Potion font_of_mana = new PotionMagicEffect(false, 0xffe5bb, 8).setBeneficial(); + public static final Potion fear = new PotionMagicEffect(true, 0xbd0100, 9); + + /** + * Sets both the registry and unlocalised names of the given potion, then registers it with the given registry. Use + * this instead of {@link potion#setRegistryName(String)} and {@link potion#setUnlocalizedName(String)} during + * construction, for convenience and consistency. + * @param registry The registry to register the given potion to. + * @param potion The potion to register. + * @param name The name of the potion, without the mod ID or the .name stuff. The registry name will be + * {@code wizardry:[name]}. The unlocalised name will be {@code potion.wizardry:[name].name}. + */ + public static void registerPotion(IForgeRegistry registry, Potion potion, String name){ + potion.setRegistryName(Wizardry.MODID, name); + // For some reason, Potion#getName() doesn't prepend "potion." itself, so it has to be done here. + potion.setPotionName("potion." + potion.getRegistryName().toString()); + registry.register(potion); + } + + @SubscribeEvent + public static void register(RegistryEvent.Register event){ + + IForgeRegistry registry = event.getRegistry(); + + registerPotion(registry, frost, "frost"); + registerPotion(registry, transience, "transience"); + registerPotion(registry, fireskin, "fireskin"); + registerPotion(registry, ice_shroud, "ice_shroud"); + registerPotion(registry, static_aura, "static_aura"); + registerPotion(registry, decay, "decay"); + registerPotion(registry, sixth_sense, "sixth_sense"); + registerPotion(registry, arcane_jammer, "arcane_jammer"); + registerPotion(registry, mind_trick, "mind_trick"); + registerPotion(registry, mind_control, "mind_control"); + registerPotion(registry, font_of_mana, "font_of_mana"); + registerPotion(registry, fear, "fear"); + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryRegistry.java b/src/main/java/electroblob/wizardry/registry/WizardryRegistry.java new file mode 100644 index 00000000..f8103bf1 --- /dev/null +++ b/src/main/java/electroblob/wizardry/registry/WizardryRegistry.java @@ -0,0 +1,320 @@ +package electroblob.wizardry.registry; + +import java.util.ArrayList; +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.EntityArc; +import electroblob.wizardry.entity.EntityMeteor; +import electroblob.wizardry.entity.EntityShield; +import electroblob.wizardry.entity.construct.EntityArrowRain; +import electroblob.wizardry.entity.construct.EntityBlackHole; +import electroblob.wizardry.entity.construct.EntityBlizzard; +import electroblob.wizardry.entity.construct.EntityBubble; +import electroblob.wizardry.entity.construct.EntityDecay; +import electroblob.wizardry.entity.construct.EntityEarthquake; +import electroblob.wizardry.entity.construct.EntityFireRing; +import electroblob.wizardry.entity.construct.EntityFireSigil; +import electroblob.wizardry.entity.construct.EntityForcefield; +import electroblob.wizardry.entity.construct.EntityFrostSigil; +import electroblob.wizardry.entity.construct.EntityHailstorm; +import electroblob.wizardry.entity.construct.EntityHammer; +import electroblob.wizardry.entity.construct.EntityHealAura; +import electroblob.wizardry.entity.construct.EntityIceSpike; +import electroblob.wizardry.entity.construct.EntityLightningPulse; +import electroblob.wizardry.entity.construct.EntityLightningSigil; +import electroblob.wizardry.entity.construct.EntityTornado; +import electroblob.wizardry.entity.living.EntityBlazeMinion; +import electroblob.wizardry.entity.living.EntityDecoy; +import electroblob.wizardry.entity.living.EntityEvilWizard; +import electroblob.wizardry.entity.living.EntityIceGiant; +import electroblob.wizardry.entity.living.EntityIceWraith; +import electroblob.wizardry.entity.living.EntityLightningWraith; +import electroblob.wizardry.entity.living.EntityMagicSlime; +import electroblob.wizardry.entity.living.EntityPhoenix; +import electroblob.wizardry.entity.living.EntityShadowWraith; +import electroblob.wizardry.entity.living.EntitySilverfishMinion; +import electroblob.wizardry.entity.living.EntitySkeletonMinion; +import electroblob.wizardry.entity.living.EntitySpiderMinion; +import electroblob.wizardry.entity.living.EntitySpiritHorse; +import electroblob.wizardry.entity.living.EntitySpiritWolf; +import electroblob.wizardry.entity.living.EntityStormElemental; +import electroblob.wizardry.entity.living.EntityWizard; +import electroblob.wizardry.entity.living.EntityZombieMinion; +import electroblob.wizardry.entity.projectile.EntityDarknessOrb; +import electroblob.wizardry.entity.projectile.EntityDart; +import electroblob.wizardry.entity.projectile.EntityFirebolt; +import electroblob.wizardry.entity.projectile.EntityFirebomb; +import electroblob.wizardry.entity.projectile.EntityForceArrow; +import electroblob.wizardry.entity.projectile.EntityForceOrb; +import electroblob.wizardry.entity.projectile.EntityIceCharge; +import electroblob.wizardry.entity.projectile.EntityIceLance; +import electroblob.wizardry.entity.projectile.EntityIceShard; +import electroblob.wizardry.entity.projectile.EntityLightningArrow; +import electroblob.wizardry.entity.projectile.EntityLightningDisc; +import electroblob.wizardry.entity.projectile.EntityMagicMissile; +import electroblob.wizardry.entity.projectile.EntityPoisonBomb; +import electroblob.wizardry.entity.projectile.EntitySmokeBomb; +import electroblob.wizardry.entity.projectile.EntitySpark; +import electroblob.wizardry.entity.projectile.EntitySparkBomb; +import electroblob.wizardry.entity.projectile.EntityThunderbolt; +import electroblob.wizardry.loot.RandomSpell; +import electroblob.wizardry.loot.WizardSpell; +import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; +import electroblob.wizardry.tileentity.TileEntityMagicLight; +import electroblob.wizardry.tileentity.TileEntityPlayerSave; +import electroblob.wizardry.tileentity.TileEntityStatue; +import electroblob.wizardry.tileentity.TileEntityTimer; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EnumCreatureType; +import net.minecraft.init.Biomes; +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.storage.loot.LootTableList; +import net.minecraft.world.storage.loot.functions.LootFunctionManager; +import net.minecraftforge.fml.common.registry.EntityRegistry; +import net.minecraftforge.fml.common.registry.GameRegistry; +import net.minecraftforge.oredict.OreDictionary; + +/** + * Class responsible for registering all the things that don't have (or need) instances: entities, loot tables, + * recipes, etc. + * @author Electroblob + * @since Wizardry 1.0 + */ +public final class WizardryRegistry { + + // NOTE: In 1.12, recipes have a registry (they can still stay here though since we don't keep references to them) + + /** Called from the preInit method in the main mod class to register the custom dungeon loot. */ + public static void registerLoot(){ + + /* Loot tables work as follows: + * Minecraft goes through each pool in turn. For each pool, it does a certain number or rolls, which can either + * be set to always be one number or a random number from a range. Each roll, it generates one stack of a single + * random entry in that pool, weighted according to the weights of the entries. Functions allow properties of + * that stack (stack size, damage, nbt) to be set, and even allow it to be replaced dynamically with a + * completely different item (though there's very little point in doing that as it could be achieved just as + * easily with more entries, which makes me think it would be bad practice). + * You can also use conditions to control whether an entry or pool is used at all, which is mostly for mob drops + * under specific conditions, but one of them is simply a random chance, meaning you could use it to make a pool + * that only gets rolled sometimes. + * All in all, this can get rather confusing, because stackable items can have 5 stages of randomness applied + * to them at once: a random chance for the pool, a random number of rolls for the pool, the weighted random + * chance of choosing that particular entry, a random chance for that entry, and a random stack size, and that's + * before you take functions into account. + * + * ...oh, and entries can be entire loot tables in themselves, allowing for potentially infinite levels of + * randomness. Yeah. + * + * Translating to the new system: + * ChestGenHooks.SOME_NAME -> loot table json file + * ??? -> loot pool (I don't think it was split up like this before) + * ChestGenHooks.addItem() -> entry in a loot pool + * Stack sizes in WeightedRandomChestContent -> set_count function for entries + * Weight in WeightedRandomChestContent -> weight of entries + * Custom WeightedRandomChestContent implementations -> custom loot functions, but only for complex/dynamic + * stuff - things that serve to allow the chance for a category of items (like armour) to be specified but still + * have a random chance for which exact item you get should be done with nested loot tables. + */ + + // Always registers the loot tables, but only injects the additions into vanilla if the appropriate option is + // enabled in the config (see WizardryEventHandler). + LootFunctionManager.registerFunction(new RandomSpell.Serializer()); + LootFunctionManager.registerFunction(new WizardSpell.Serializer()); + LootTableList.register(new ResourceLocation(Wizardry.MODID, "chests/wizard_tower")); + LootTableList.register(new ResourceLocation(Wizardry.MODID, "chests/dungeon_additions")); + LootTableList.register(new ResourceLocation(Wizardry.MODID, "subsets/novice_wands")); + LootTableList.register(new ResourceLocation(Wizardry.MODID, "subsets/wizard_armour")); + LootTableList.register(new ResourceLocation(Wizardry.MODID, "subsets/arcane_tomes")); + LootTableList.register(new ResourceLocation(Wizardry.MODID, "subsets/wand_upgrades")); + LootTableList.register(new ResourceLocation(Wizardry.MODID, "entities/evil_wizard")); + // TODO: At the moment this is not used anywhere because I can't find a way to add it to all mobs. + //LootTableList.register(new ResourceLocation(Wizardry.MODID, "entities/mob_additions")); + + } + + /** Called from the preInit method in the main mod class to register all the tile entities. */ + public static void registerTileEntities(){ + + GameRegistry.registerTileEntity(TileEntityArcaneWorkbench.class, Wizardry.MODID + "ArcaneWorkbenchTileEntity"); + GameRegistry.registerTileEntity(TileEntityStatue.class, Wizardry.MODID + "PetrifiedStoneTileEntity"); + GameRegistry.registerTileEntity(TileEntityMagicLight.class, Wizardry.MODID + "MagicLightTileEntity"); + GameRegistry.registerTileEntity(TileEntityTimer.class, Wizardry.MODID + "TimerTileEntity"); + GameRegistry.registerTileEntity(TileEntityPlayerSave.class, Wizardry.MODID + "TileEntityPlayerSave"); + } + + /** Not actually the frequency at all; smaller numbers are more frequent. Vanilla uses 3 I think. */ + private static final int LIVING_UPDATE_INTERVAL = 3; + /** Not actually the frequency at all; smaller numbers are more frequent. */ + private static final int PROJECTILE_UPDATE_INTERVAL = 10; + + /** Called from the preInit method in the main mod class to register all the entities. */ + public static void registerEntities(Wizardry wizardry){ + + int id = 0; // Incrementable index for the mod specific entity id. + + EntityRegistry.registerModEntity(EntityZombieMinion.class, "zombie_minion", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityMagicMissile.class, "magic_missile", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + // TODO: This should be a particle + EntityRegistry.registerModEntity(EntityArc.class, "arc", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); + EntityRegistry.registerModEntity(EntitySkeletonMinion.class, "skeleton_minion", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntitySparkBomb.class, "spark_bomb", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntitySpiritWolf.class, "spirit_wolf", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityIceShard.class, "ice_shard", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityBlazeMinion.class, "blaze_minion", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityIceWraith.class, "ice_wraith", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityLightningWraith.class, "lightning_wraith", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityBlackHole.class, "black_hole", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); + EntityRegistry.registerModEntity(EntityShield.class, "shield", id++, wizardry, 128, 1, true); + EntityRegistry.registerModEntity(EntityMeteor.class, "meteor", id++, wizardry, 128, 5, true); + EntityRegistry.registerModEntity(EntityBlizzard.class, "blizzard", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); + EntityRegistry.registerModEntity(EntityWizard.class, "wizard", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityBubble.class, "bubble", id++, wizardry, 128, 3, false); + EntityRegistry.registerModEntity(EntityTornado.class, "tornado", id++, wizardry, 128, 1, false); + EntityRegistry.registerModEntity(EntityHammer.class, "lightning_hammer", id++, wizardry, 128, 1, true); + EntityRegistry.registerModEntity(EntityFirebomb.class, "firebomb", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityForceOrb.class, "force_orb", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityArrowRain.class, "arrow_rain", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); + EntityRegistry.registerModEntity(EntitySpark.class, "spark", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityShadowWraith.class, "shadow_wraith", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityDarknessOrb.class, "darkness_orb", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntitySpiderMinion.class, "spider_minion", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityHealAura.class, "healing_aura", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); + EntityRegistry.registerModEntity(EntityFireSigil.class, "fire_sigil", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); + EntityRegistry.registerModEntity(EntityFrostSigil.class, "frost_sigil", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); + EntityRegistry.registerModEntity(EntityLightningSigil.class, "lightning_sigil", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); + EntityRegistry.registerModEntity(EntityLightningArrow.class, "lightning_arrow", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityFirebolt.class, "firebolt", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityPoisonBomb.class, "poison_bomb", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityIceCharge.class, "ice_charge", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityForceArrow.class, "force_arrow", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityDart.class, "dart", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityMagicSlime.class, "magic_slime", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityForcefield.class, "forcefield", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); + EntityRegistry.registerModEntity(EntityFireRing.class, "ring_of_fire", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); + EntityRegistry.registerModEntity(EntityLightningDisc.class, "lightning_disc", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityThunderbolt.class, "thunderbolt", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityIceGiant.class, "ice_giant", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntitySpiritHorse.class, "spirit_horse", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityPhoenix.class, "phoenix", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntitySilverfishMinion.class, "silverfish_minion", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityDecay.class, "decay", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); + EntityRegistry.registerModEntity(EntityStormElemental.class, "storm_elemental", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityEarthquake.class, "earthquake", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); + EntityRegistry.registerModEntity(EntityIceLance.class, "ice_lance", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityHailstorm.class, "hailstorm", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); + EntityRegistry.registerModEntity(EntitySmokeBomb.class, "smoke_bomb", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityEvilWizard.class, "evil_wizard", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityDecoy.class, "decoy", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); + EntityRegistry.registerModEntity(EntityIceSpike.class, "ice_spike", id++, wizardry, 128, 1, true); + // TODO: This should be a particle. + EntityRegistry.registerModEntity(EntityLightningPulse.class, "lightning_pulse", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); + + EntityRegistry.registerEgg(EntityWizard.class, 0x19295e, 0xee9312); + EntityRegistry.registerEgg(EntityEvilWizard.class, 0x290404, 0xee9312); + + // TODO: May need fixing + List biomes = new ArrayList(); + for(Biome biome : Biome.EXPLORATION_BIOMES_LIST){ + if(biome != null){ + biomes.add(biome); + } + } + biomes.remove(Biomes.MUSHROOM_ISLAND); + biomes.remove(Biomes.MUSHROOM_ISLAND_SHORE); + // For reference: 5, 1, 1 are the parameters for the witch in vanilla. + EntityRegistry.addSpawn(EntityEvilWizard.class, 3, 1, 1, EnumCreatureType.MONSTER, biomes.toArray(new Biome[biomes.size()])); + + } + + /** Called from the init method in the main mod class to register all the recipes. */ + public static void registerRecipes(){ + + ItemStack magicCrystalStack = new ItemStack(WizardryItems.magic_crystal); + ItemStack magicWandStack = new ItemStack(WizardryItems.magic_wand, 1, Tier.BASIC.maxCharge); + ItemStack goldNuggetStack = new ItemStack(Items.GOLD_NUGGET); + ItemStack stickStack = new ItemStack(Items.STICK); + ItemStack bookStack = new ItemStack(Items.BOOK); + ItemStack stringStack = new ItemStack(Items.STRING); + ItemStack spellBookStack = new ItemStack(WizardryItems.spell_book, 1, Spells.magic_missile.id()); + ItemStack arcaneWorkbenchStack = new ItemStack(WizardryBlocks.arcane_workbench); + ItemStack stoneStack = new ItemStack(Blocks.STONE); + ItemStack lapisBlockStack = new ItemStack(Blocks.LAPIS_BLOCK); + ItemStack purpleCarpetStack = new ItemStack(Blocks.CARPET, 1, 10); + ItemStack wizardHandbookStack = new ItemStack(WizardryItems.wizard_handbook); + ItemStack crystalFlowerStack = new ItemStack(WizardryBlocks.crystal_flower); + ItemStack magicCrystalStack1 = new ItemStack(WizardryItems.magic_crystal, 2); + ItemStack magicCrystalStack2 = new ItemStack(WizardryItems.magic_crystal, 9); + ItemStack crystalBlockStack = new ItemStack(WizardryBlocks.crystal_block); + ItemStack manaFlaskStack = new ItemStack(WizardryItems.mana_flask); + ItemStack bottleStack = new ItemStack(Items.GLASS_BOTTLE); + ItemStack gunpowderStack = new ItemStack(Items.GUNPOWDER); + ItemStack blazePowderStack = new ItemStack(Items.BLAZE_POWDER); + ItemStack spiderEyeStack = new ItemStack(Items.SPIDER_EYE); + // Coal or charcoal is equally fine, hence the wildcard value + ItemStack coalStack = new ItemStack(Items.COAL, 1, OreDictionary.WILDCARD_VALUE); + ItemStack firebombStack = new ItemStack(WizardryItems.firebomb, 3); + ItemStack poisonBombStack = new ItemStack(WizardryItems.poison_bomb, 3); + ItemStack smokeBombStack = new ItemStack(WizardryItems.smoke_bomb, 3); + ItemStack transportationStoneStack = new ItemStack(WizardryBlocks.transportation_stone, 2); + ItemStack silkStack = new ItemStack(WizardryItems.magic_silk); + ItemStack silkStack1 = new ItemStack(WizardryItems.magic_silk, 2); + ItemStack hatStack = new ItemStack(WizardryItems.wizard_hat); + ItemStack robeStack = new ItemStack(WizardryItems.wizard_robe); + ItemStack leggingsStack = new ItemStack(WizardryItems.wizard_leggings); + ItemStack bootsStack = new ItemStack(WizardryItems.wizard_boots); + ItemStack scrollStack = new ItemStack(WizardryItems.blank_scroll); + ItemStack paperStack = new ItemStack(Items.PAPER); + + GameRegistry.addRecipe(magicWandStack, " x", " y ", "z ", 'x', magicCrystalStack, 'y', stickStack, 'z', goldNuggetStack); + GameRegistry.addRecipe(spellBookStack, " x ", "xyx", " x ", 'x', magicCrystalStack, 'y', bookStack); + GameRegistry.addRecipe(arcaneWorkbenchStack, "vwv", "xyx", "zzz", 'v', goldNuggetStack, 'w', purpleCarpetStack, 'x', magicCrystalStack, 'y', lapisBlockStack, 'z', stoneStack); + GameRegistry.addRecipe(manaFlaskStack, "yyy", "yxy", "yyy", 'x', bottleStack, 'y', magicCrystalStack); + GameRegistry.addRecipe(transportationStoneStack, " x ", "xyx", " x ", 'x', stoneStack, 'y', magicCrystalStack); + GameRegistry.addRecipe(hatStack, "yyy", "y y", 'y', silkStack); + GameRegistry.addRecipe(robeStack, "y y", "yyy", "yyy", 'y', silkStack); + GameRegistry.addRecipe(leggingsStack, "yyy", "y y", "y y", 'y', silkStack); + GameRegistry.addRecipe(bootsStack, "y y", "y y", 'y', silkStack); + GameRegistry.addRecipe(silkStack1, " x ", "xyx", " x ", 'x', stringStack, 'y', magicCrystalStack); + GameRegistry.addRecipe(crystalBlockStack, "zzz", "zzz", "zzz", 'z', magicCrystalStack); + + GameRegistry.addShapelessRecipe(wizardHandbookStack, bookStack, magicCrystalStack); + GameRegistry.addShapelessRecipe(magicCrystalStack1, crystalFlowerStack); + GameRegistry.addShapelessRecipe(magicCrystalStack2, crystalBlockStack); + if(Wizardry.settings.firebombIsCraftable) GameRegistry.addShapelessRecipe(firebombStack, bottleStack, gunpowderStack, blazePowderStack, blazePowderStack); + if(Wizardry.settings.poisonBombIsCraftable) GameRegistry.addShapelessRecipe(poisonBombStack, bottleStack, gunpowderStack, spiderEyeStack, spiderEyeStack); + if(Wizardry.settings.smokeBombIsCraftable) GameRegistry.addShapelessRecipe(smokeBombStack, bottleStack, gunpowderStack, coalStack, coalStack); + if(Wizardry.settings.useAlternateScrollRecipe){ + GameRegistry.addShapelessRecipe(scrollStack, paperStack, stringStack, magicCrystalStack); + }else{ + GameRegistry.addShapelessRecipe(scrollStack, paperStack, stringStack); + } + + // Mana flask recipes + ItemStack miscWandStack; + + for(Element element : Element.values()){ + for(Tier tier : Tier.values()){ + miscWandStack = new ItemStack(WizardryUtilities.getWand(tier, element), 1, OreDictionary.WILDCARD_VALUE); + GameRegistry.addShapelessRecipe(miscWandStack, miscWandStack, manaFlaskStack); + } + } + + ItemStack miscArmourStack; + + for(Element element : Element.values()){ + for(EntityEquipmentSlot slot : WizardryUtilities.ARMOUR_SLOTS){ + miscArmourStack = new ItemStack(WizardryUtilities.getArmour(element, slot), 1, OreDictionary.WILDCARD_VALUE); + GameRegistry.addShapelessRecipe(miscArmourStack, miscArmourStack, manaFlaskStack); + } + } + } + +} diff --git a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java new file mode 100644 index 00000000..d006add1 --- /dev/null +++ b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java @@ -0,0 +1,68 @@ +package electroblob.wizardry.registry; + +import electroblob.wizardry.Wizardry; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundEvent; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +/** + * Class responsible for defining, storing and registering all of wizardry's sound events. For some reason, these + * worked in the beta versions despite not being registered... + * @author Electroblob + * @since Wizardry 2.1 + */ +@Mod.EventBusSubscriber +public final class WizardrySounds { + + // Anything with LOOP in its name is intended to be played in a continuous loop. + public static final SoundEvent SPELL_SPARK = createSound("arc"); + public static final SoundEvent SPELL_CONJURATION = createSound("aura"); + public static final SoundEvent SPELL_SHOCKWAVE = createSound("boom"); + public static final SoundEvent SPELL_LOOP_CRACKLE = createSound("crackle"); + public static final SoundEvent SPELL_SUMMONING = createSound("darkaura"); + public static final SoundEvent SPELL_DEFLECTION = createSound("effect"); + public static final SoundEvent SPELL_LIGHTNING = createSound("electricitya"); + public static final SoundEvent SPELL_LOOP_LIGHTNING = createSound("electricityb"); + public static final SoundEvent SPELL_LOOP_FIRE = createSound("flameray"); + public static final SoundEvent SPELL_FREEZE = createSound("freeze"); + public static final SoundEvent SPELL_LOOP_ICE = createSound("frostray"); + public static final SoundEvent SPELL_HEAL = createSound("heal"); + public static final SoundEvent SPELL_ICE = createSound("ice"); + public static final SoundEvent SPELL_CONJURATION_LARGE = createSound("largeaura"); + public static final SoundEvent SPELL_MAGIC = createSound("magic"); + public static final SoundEvent SPELL_LOOP_SPARKLE = createSound("sparkle"); + public static final SoundEvent SPELL_LOOP_WIND = createSound("wind"); + public static final SoundEvent SPELL_EARTHQUAKE = createSound("rumble"); + public static final SoundEvent SPELL_FORCE = createSound("force"); + + /** Trick borrowed from the Twilight Forest, makes things neater. */ + public static SoundEvent createSound(String name){ + // All the setRegistryName methods delegate to this one, it doesn't matter which you use. + return new SoundEvent(new ResourceLocation(Wizardry.MODID, name)).setRegistryName(name); + } + + @SubscribeEvent + public static void register(RegistryEvent.Register event){ + event.getRegistry().register(SPELL_SPARK); + event.getRegistry().register(SPELL_CONJURATION); + event.getRegistry().register(SPELL_SHOCKWAVE); + event.getRegistry().register(SPELL_LOOP_CRACKLE); + event.getRegistry().register(SPELL_SUMMONING); + event.getRegistry().register(SPELL_DEFLECTION); + event.getRegistry().register(SPELL_LIGHTNING); + event.getRegistry().register(SPELL_LOOP_LIGHTNING); + event.getRegistry().register(SPELL_LOOP_FIRE); + event.getRegistry().register(SPELL_FREEZE); + event.getRegistry().register(SPELL_LOOP_ICE); + event.getRegistry().register(SPELL_HEAL); + event.getRegistry().register(SPELL_ICE); + event.getRegistry().register(SPELL_CONJURATION_LARGE); + event.getRegistry().register(SPELL_MAGIC); + event.getRegistry().register(SPELL_LOOP_SPARKLE); + event.getRegistry().register(SPELL_LOOP_WIND); + event.getRegistry().register(SPELL_EARTHQUAKE); + event.getRegistry().register(SPELL_FORCE); + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryTabs.java b/src/main/java/electroblob/wizardry/registry/WizardryTabs.java new file mode 100644 index 00000000..6eeb9fc6 --- /dev/null +++ b/src/main/java/electroblob/wizardry/registry/WizardryTabs.java @@ -0,0 +1,175 @@ +package electroblob.wizardry.registry; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import com.google.common.base.Function; +import com.google.common.collect.Ordering; + +import electroblob.wizardry.item.ItemScroll; +import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.spell.Spell; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** + * Class responsible for defining and storing all of wizardry's creative tabs. Also handles sorting of the items. + * @author Electroblob + * @since Wizardry 2.1 + */ +public final class WizardryTabs { + + private static Comparator itemSorter; + private static Comparator spellItemSorter; + + // Creative Tabs + public static final CreativeTabs WIZARDRY = new CreativeTabs("wizardry"){ + + @Override + @SideOnly(Side.CLIENT) + public Item getTabIconItem() { + return WizardryItems.wizard_handbook; + } + + @Override + @SideOnly(Side.CLIENT) + public void displayAllRelevantItems(List items) { + super.displayAllRelevantItems(items); + Collections.sort(items, itemSorter); + } + }; + public static final CreativeTabs SPELLS = new CreativeTabs("wizardryspells"){ + + @Override + @SideOnly(Side.CLIENT) + public Item getTabIconItem() { + return WizardryItems.spell_book; + } + + @Override + @SideOnly(Side.CLIENT) + public void displayAllRelevantItems(List items) { + super.displayAllRelevantItems(items); + Collections.sort(items, spellItemSorter); + } + + @Override + public boolean hasSearchBar() { + return true; + } + + @Override + @SideOnly(Side.CLIENT) + public String getBackgroundImageName() { + return "item_search.png"; + } + }; + + /** Initialises the item sorters for the creative tabs. */ + public static void sort(){ + + List orderedItemList = Arrays.asList( + + Item.getItemFromBlock(WizardryBlocks.arcane_workbench), + Item.getItemFromBlock(WizardryBlocks.crystal_ore), + Item.getItemFromBlock(WizardryBlocks.crystal_block), + Item.getItemFromBlock(WizardryBlocks.crystal_flower), + Item.getItemFromBlock(WizardryBlocks.transportation_stone), + WizardryItems.magic_crystal, + WizardryItems.magic_wand, + WizardryItems.apprentice_wand, + WizardryItems.advanced_wand, + WizardryItems.master_wand, + WizardryItems.arcane_tome, + WizardryItems.wizard_handbook, + WizardryItems.basic_fire_wand, + WizardryItems.basic_ice_wand, + WizardryItems.basic_lightning_wand, + WizardryItems.basic_necromancy_wand, + WizardryItems.basic_earth_wand, + WizardryItems.basic_sorcery_wand, + WizardryItems.basic_healing_wand, + WizardryItems.smoke_bomb, + WizardryItems.firebomb, + WizardryItems.poison_bomb, + WizardryItems.blank_scroll, + WizardryItems.identification_scroll, + WizardryItems.mana_flask, + WizardryItems.storage_upgrade, + WizardryItems.siphon_upgrade, + WizardryItems.condenser_upgrade, + WizardryItems.range_upgrade, + WizardryItems.duration_upgrade, + WizardryItems.cooldown_upgrade, + WizardryItems.blast_upgrade, + WizardryItems.attunement_upgrade, + WizardryItems.magic_silk, + WizardryItems.armour_upgrade, + WizardryItems.wizard_hat, + WizardryItems.wizard_robe, + WizardryItems.wizard_leggings, + WizardryItems.wizard_boots, + WizardryItems.wizard_hat_fire, + WizardryItems.wizard_robe_fire, + WizardryItems.wizard_leggings_fire, + WizardryItems.wizard_boots_fire, + WizardryItems.wizard_hat_ice, + WizardryItems.wizard_robe_ice, + WizardryItems.wizard_leggings_ice, + WizardryItems.wizard_boots_ice, + WizardryItems.wizard_hat_lightning, + WizardryItems.wizard_robe_lightning, + WizardryItems.wizard_leggings_lightning, + WizardryItems.wizard_boots_lightning, + WizardryItems.wizard_hat_necromancy, + WizardryItems.wizard_robe_necromancy, + WizardryItems.wizard_leggings_necromancy, + WizardryItems.wizard_boots_necromancy, + WizardryItems.wizard_hat_earth, + WizardryItems.wizard_robe_earth, + WizardryItems.wizard_leggings_earth, + WizardryItems.wizard_boots_earth, + WizardryItems.wizard_hat_sorcery, + WizardryItems.wizard_robe_sorcery, + WizardryItems.wizard_leggings_sorcery, + WizardryItems.wizard_boots_sorcery, + WizardryItems.wizard_hat_healing, + WizardryItems.wizard_robe_healing, + WizardryItems.wizard_leggings_healing, + WizardryItems.wizard_boots_healing); + + itemSorter = Ordering.explicit(orderedItemList).onResultOf(new Function() { + @Override + public Item apply(ItemStack input) { + return input.getItem(); + } + }); + + spellItemSorter = new Comparator(){ + @Override + public int compare(ItemStack stack1, ItemStack stack2) { + + if((stack1.getItem() instanceof ItemSpellBook && stack2.getItem() instanceof ItemSpellBook) + || (stack1.getItem() instanceof ItemScroll && stack2.getItem() instanceof ItemScroll)){ + + Spell spell1 = Spell.get(stack1.getItemDamage()); + Spell spell2 = Spell.get(stack2.getItemDamage()); + + return spell1.compareTo(spell2); + + }else if(stack1.getItem() instanceof ItemScroll){ + return 1; + }else if(stack2.getItem() instanceof ItemScroll){ + return -1; + } + return 0; + } + }; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Agility.java b/src/main/java/electroblob/wizardry/spell/Agility.java new file mode 100644 index 00000000..db01f4a4 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Agility.java @@ -0,0 +1,46 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class Agility extends Spell { + + public Agility() { + super(Tier.APPRENTICE, 20, Element.SORCERY, "agility", SpellType.UTILITY, 40, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + // 1.10 allows the particles to be completely hidden. + caster.addPotionEffect(new PotionEffect(MobEffects.SPEED, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 1, false, false)); + caster.addPotionEffect(new PotionEffect(MobEffects.JUMP_BOOST, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 1, false, false)); + + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + if(world.isRemote){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Arc.java b/src/main/java/electroblob/wizardry/spell/Arc.java new file mode 100644 index 00000000..372911d4 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Arc.java @@ -0,0 +1,105 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.EntityArc; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +// This spell was the 'guinea pig' for damage types, so to speak, so there's a bit of commentary on them here that may +// be useful for future reference. +public class Arc extends Spell { + + public Arc() { + super(Tier.BASIC, 5, Element.LIGHTNING, "arc", SpellType.ATTACK, 15, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*modifiers.get(WizardryItems.range_upgrade), 4.0f); + + if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){ + + Entity target = rayTrace.entityHit; + + if(!world.isRemote){ + EntityArc arc = new EntityArc(world); + arc.setEndpointCoords(caster.posX, caster.posY + 1, caster.posZ, + target.posX, target.posY + target.height/2, target.posZ); + world.spawnEntityInWorld(arc); + }else{ + for(int i=0;i<8;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); + } + } + + // This is a lot neater than it was, thanks to the damage type system. + if(MagicDamage.isEntityImmune(DamageType.SHOCK, target)){ + if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + }else{ + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 3.0f * modifiers.get(SpellModifiers.DAMAGE)); + } + + caster.swingArm(hand); + target.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); + return true; + } + + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + EntityArc arc = new EntityArc(world); + arc.setEndpointCoords(caster.posX, caster.posY + 1, caster.posZ, + target.posX, target.posY + target.height/2, target.posZ); + world.spawnEntityInWorld(arc); + }else{ + for(int i=0;i<8;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); + } + } + + // What's great about the damage type system is that, because I don't need to know if the creature resisted + // the damage here, I can simply call this without having to check for immunities at all. + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 3.0f * modifiers.get(SpellModifiers.DAMAGE)); + + caster.swingArm(hand); + target.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java b/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java new file mode 100644 index 00000000..30bd9cca --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java @@ -0,0 +1,95 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntityWizard; +import electroblob.wizardry.registry.WizardryAchievements; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class ArcaneJammer extends Spell { + + public ArcaneJammer() { + super(Tier.ADVANCED, 30, Element.HEALING, "arcane_jammer", SpellType.ATTACK, 50, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + Vec3d look = caster.getLookVec(); + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + + EntityLivingBase entity = (EntityLivingBase) rayTrace.entityHit; + if(entity instanceof EntityWizard) caster.addStat(WizardryAchievements.jam_wizard); + + if(!world.isRemote){ + entity.addPotionEffect(new PotionEffect(WizardryPotions.arcane_jammer, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0)); + } + } + if(world.isRemote){ + for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.9f, 0.3f, 0.7f); + } + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_DEFLECTION, 0.7F, world.rand.nextFloat() * 0.4F + 0.8F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + if(!world.isRemote){ + target.addPotionEffect(new PotionEffect(WizardryPotions.arcane_jammer, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0)); + } + + if(world.isRemote){ + + double dx = (target.posX - caster.posX)/caster.getDistanceToEntity(target); + double dy = (target.posY - caster.posY)/caster.getDistanceToEntity(target); + double dz = (target.posZ - caster.posZ)/caster.getDistanceToEntity(target); + + for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + + double x1 = caster.posX + dx*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = caster.posY + caster.getEyeHeight() - 0.4f + dy*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + dz*i/2 + world.rand.nextFloat()/5 - 0.1f; + + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.9f, 0.3f, 0.7f); + } + } + caster.swingArm(hand); + caster.playSound(WizardrySounds.SPELL_DEFLECTION, 0.7F, world.rand.nextFloat() * 0.4F + 0.8F); + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/ArrowRain.java b/src/main/java/electroblob/wizardry/spell/ArrowRain.java new file mode 100644 index 00000000..b8c8f210 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/ArrowRain.java @@ -0,0 +1,57 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.construct.EntityArrowRain; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class ArrowRain extends Spell { + + public ArrowRain() { + super(Tier.MASTER, 75, Element.SORCERY, "arrow_rain", SpellType.ATTACK, 300, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.rayTrace(20*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(!world.isRemote){ + double x = rayTrace.hitVec.xCoord; + double y = rayTrace.hitVec.yCoord; + double z = rayTrace.hitVec.zCoord; + // Moves the entity back towards the caster a bit, so the area of effect is better centred on the position. + // 3.0d is the distance to move the entity back towards the caster. + double dx = caster.posX - x; + double dz = caster.posZ - z; + double distRatio = 3.0d/Math.sqrt(dx*dx + dz*dz); + x += dx*distRatio; + z += dz*distRatio; + + EntityArrowRain arrowrain = new EntityArrowRain(world, x, y + 5, z, caster, (int)(120*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + arrowrain.rotationYaw = caster.rotationYawHead; + world.spawnEntityInWorld(arrowrain); + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 1.0F, 1.0F); + return true; + } + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Banish.java b/src/main/java/electroblob/wizardry/spell/Banish.java new file mode 100644 index 00000000..8bf92bec --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Banish.java @@ -0,0 +1,159 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class Banish extends Spell { + + public Banish() { + super(Tier.APPRENTICE, 15, Element.NECROMANCY, "banish", SpellType.ATTACK, 40, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + Vec3d look = caster.getLookVec(); + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + + EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; + + double radius = (8 + world.rand.nextDouble()*8) * modifiers.get(WizardryItems.range_upgrade); + double angle = world.rand.nextDouble()*Math.PI*2; + + int x = MathHelper.floor_double(target.posX + Math.sin(angle)*radius); + int z = MathHelper.floor_double(target.posZ - Math.cos(angle)*radius); + int y = WizardryUtilities.getNearestFloorLevel(world, new BlockPos(x, (int)caster.getEntityBoundingBox().minY, z), (int)radius); + + if(world.isRemote){ + for(int i=0;i<10;i++){ + double dx1 = target.posX; + double dy1 = target.getEntityBoundingBox().minY + target.height*world.rand.nextFloat(); + double dz1 = target.posZ; + world.spawnParticle(EnumParticleTypes.PORTAL, dx1, dy1, dz1, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); + } + } + + if(y > -1){ + + // This means stuff like snow layers is ignored, meaning when on snow-covered ground the caster does + // not teleport 1 block above the ground. + if(!world.getBlockState(new BlockPos(x, y, z)).getMaterial().blocksMovement()){ + y--; + } + + if(world.getBlockState(new BlockPos(x, y + 1, z)).getMaterial().blocksMovement() || world.getBlockState(new BlockPos(x, y + 2, z)).getMaterial().blocksMovement()){ + return false; + } + + if(!world.isRemote){ + target.setPositionAndUpdate(x + 0.5, y + 1, z + 0.5); + } + + target.playSound(SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); + } + } + + if(world.isRemote){ + for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + + world.spawnParticle(EnumParticleTypes.PORTAL, x1, y1 - 0.5, z1, 0.0d, 0.0d, 0.0d); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.2f, 0.0f, 0.2f); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); + caster.swingArm(hand); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + double radius = (8 + world.rand.nextDouble()*8) * modifiers.get(WizardryItems.range_upgrade); + double angle = world.rand.nextDouble()*Math.PI*2; + + int x = MathHelper.floor_double(target.posX + Math.sin(angle)*radius); + int z = MathHelper.floor_double(target.posZ - Math.cos(angle)*radius); + int y = WizardryUtilities.getNearestFloorLevel(world, new BlockPos(x, (int)caster.getEntityBoundingBox().minY, z), (int)radius); + + if(world.isRemote){ + + double dx = (target.posX - caster.posX)/caster.getDistanceToEntity(target); + double dy = (target.posY - caster.posY)/caster.getDistanceToEntity(target); + double dz = (target.posZ - caster.posZ)/caster.getDistanceToEntity(target); + + for(int i=1; i<25; i+=2){ + + double x1 = caster.posX + dx*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = caster.posY + caster.getEyeHeight() - 0.4f + dy*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + dz*i/2 + world.rand.nextFloat()/5 - 0.1f; + + world.spawnParticle(EnumParticleTypes.PORTAL, x1, y1 - 0.5, z1, 0.0d, 0.0d, 0.0d); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.2f, 0.0f, 0.2f); + } + + for(int i=0;i<10;i++){ + double dx1 = target.posX; + double dy1 = target.getEntityBoundingBox().minY + target.height*world.rand.nextFloat(); + double dz1 = target.posZ; + world.spawnParticle(EnumParticleTypes.PORTAL, dx1, dy1, dz1, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); + } + } + + if(y > -1){ + + // This means stuff like snow layers is ignored, meaning when on snow-covered ground the caster does + // not teleport 1 block above the ground. + if(!world.getBlockState(new BlockPos(x, y, z)).getMaterial().blocksMovement()){ + y--; + } + + if(world.getBlockState(new BlockPos(x, y + 1, z)).getMaterial().blocksMovement() || world.getBlockState(new BlockPos(x, y + 2, z)).getMaterial().blocksMovement()){ + return false; + } + + if(!world.isRemote){ + target.setPositionAndUpdate(x + 0.5, y + 1, z + 0.5); + } + + target.playSound(SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); + } + } + + caster.playSound(SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); + caster.swingArm(hand); + return true; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/BlackHole.java b/src/main/java/electroblob/wizardry/spell/BlackHole.java new file mode 100644 index 00000000..3cfcd267 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/BlackHole.java @@ -0,0 +1,66 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.construct.EntityBlackHole; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class BlackHole extends Spell { + + public BlackHole() { + super(Tier.MASTER, 150, Element.SORCERY, "black_hole", SpellType.ATTACK, 400, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.rayTrace(10*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + + // This demonstrates beautifully the elegance of BlockPos. In 1.7.10 this required a 50-line long switch + // statement and a flag variable; now it only needs a few lines. + BlockPos pos = new BlockPos(rayTrace.hitVec).offset(rayTrace.sideHit); + + if(world.isAirBlock(pos)){ + + if(!world.isRemote){ + world.spawnEntityInWorld(new EntityBlackHole(world, pos.getX()+0.5, pos.getY()-1+0.5, pos.getZ()+0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE))); + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 2.0f, 0.7f); + return true; + } + + }else{ + int x = (int) (Math.floor(caster.posX) + caster.getLookVec().xCoord*8); + int y = (int) (Math.floor(caster.posY) + caster.eyeHeight + caster.getLookVec().yCoord*8); + int z = (int) (Math.floor(caster.posZ) + caster.getLookVec().zCoord*8); + if(!world.isRemote){ + world.spawnEntityInWorld(new EntityBlackHole(world, x, y, z, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE))); + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 2.0f, 0.7f); + return true; + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Blink.java b/src/main/java/electroblob/wizardry/spell/Blink.java new file mode 100644 index 00000000..3353d686 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Blink.java @@ -0,0 +1,134 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class Blink extends Spell { + + public Blink() { + super(Tier.APPRENTICE, 15, Element.SORCERY, "blink", SpellType.UTILITY, 25, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.rayTrace(25*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + + // It's worth noting that on the client side, the cast() method only gets called if the server side + // cast method succeeded, so you need not check any conditions for spawning particles. + if(world.isRemote){ + for(int i=0;i<10;i++){ + double dx = caster.posX; + double dy = caster.getEntityBoundingBox().minY + 2*world.rand.nextFloat(); + double dz = caster.posZ; + // For portal particles, velocity is not velocity but the offset where they start, then drift to + // the actual position given. + world.spawnParticle(EnumParticleTypes.PORTAL, dx, dy, dz, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); + } + } + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + + BlockPos pos = rayTrace.getBlockPos(); + + // Can't teleport onto the ceiling. + if(rayTrace.sideHit == EnumFacing.DOWN) return false; + + // This means stuff like snow layers is ignored, meaning when on snow-covered ground the player does + // not teleport 1 block above the ground. + if(rayTrace.sideHit == EnumFacing.UP && !world.getBlockState(pos).getMaterial().blocksMovement()){ + pos = pos.down(); + } + + pos = rayTrace.getBlockPos().offset(rayTrace.sideHit); + + // Prevents the player from teleporting into blocks and suffocating. + if(world.getBlockState(pos).getMaterial().blocksMovement() || world.getBlockState(pos.up()).getMaterial().blocksMovement()){ + return false; + } + + // Plays before and after so it is heard from both positions + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); + + if(!world.isRemote) caster.setPositionAndUpdate(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); + caster.swingArm(hand); + return true; + } + + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + double angle = Math.atan2(target.posZ - caster.posZ, target.posX - caster.posX) + world.rand.nextDouble()*Math.PI; + double radius = caster.getDistance(target.posX, target.getEntityBoundingBox().minY, target.posZ) + world.rand.nextDouble()*3.0d; + + int x = MathHelper.floor_double(target.posX + Math.sin(angle)*radius); + int z = MathHelper.floor_double(target.posZ - Math.cos(angle)*radius); + int y = WizardryUtilities.getNearestFloorLevel(world, new BlockPos(caster), (int)radius); + + // It's worth noting that on the client side, the cast() method only gets called if the server side + // cast method succeeded, so you need not check any conditions for spawning particles. + + // For some reason, the wizard version spwans the particles where the wizard started + if(world.isRemote){ + for(int i=0;i<10;i++){ + double dx1 = caster.posX; + double dy1 = caster.getEntityBoundingBox().minY + caster.height*world.rand.nextFloat(); + double dz1 = caster.posZ; + world.spawnParticle(EnumParticleTypes.PORTAL, dx1, dy1, dz1, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); + } + } + + if(y > -1){ + + // This means stuff like snow layers is ignored, meaning when on snow-covered ground the caster does + // not teleport 1 block above the ground. + if(!world.getBlockState(new BlockPos(x, y, z)).getMaterial().blocksMovement()){ + y--; + } + + if(world.getBlockState(new BlockPos(x, y + 1, z)).getMaterial().blocksMovement() || world.getBlockState(new BlockPos(x, y + 2, z)).getMaterial().blocksMovement()){ + return false; + } + + // Plays before and after so it is heard from both positions + caster.playSound(SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); + + if(!world.isRemote){ + caster.setPositionAndUpdate(x + 0.5, y + 1, z + 0.5); + } + + caster.playSound(SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); + caster.swingArm(hand); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs() { + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Blizzard.java b/src/main/java/electroblob/wizardry/spell/Blizzard.java new file mode 100644 index 00000000..8f2066db --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Blizzard.java @@ -0,0 +1,75 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.construct.EntityBlizzard; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class Blizzard extends Spell { + + public Blizzard() { + super(Tier.ADVANCED, 40, Element.ICE, "blizzard", SpellType.ATTACK, 100, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.rayTrace(20*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(!world.isRemote){ + double x = rayTrace.hitVec.xCoord; + double y = rayTrace.hitVec.yCoord; + double z = rayTrace.hitVec.zCoord; + EntityBlizzard blizzard = new EntityBlizzard(world, x, y+0.5, z, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(blizzard); + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + return true; + } + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + double x = target.posX; + double y = target.posY; + double z = target.posZ; + EntityBlizzard blizzard = new EntityBlizzard(world, x, y+0.5, z, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(blizzard); + } + caster.swingArm(hand); + caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Bubble.java b/src/main/java/electroblob/wizardry/spell/Bubble.java new file mode 100644 index 00000000..c9c425d1 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Bubble.java @@ -0,0 +1,110 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.construct.EntityBubble; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class Bubble extends Spell { + + public Bubble() { + super(Tier.APPRENTICE, 15, Element.EARTH, "bubble", SpellType.ATTACK, 20, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + Vec3d look = caster.getLookVec(); + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + EntityLivingBase entity = (EntityLivingBase) rayTrace.entityHit; + if(!world.isRemote){ + entity.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.MAGIC), 1.0f * modifiers.get(SpellModifiers.DAMAGE)); + // Deprecated in favour of entity riding method + //entity.addPotionEffect(new PotionEffect(Wizardry.bubblePotion, 200, 0)); + EntityBubble entitybubble = new EntityBubble(world, entity.posX, entity.posY, entity.posZ, caster, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), false, modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(entitybubble); + entity.startRiding(entitybubble); + } + } + if(world.isRemote){ + for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + + world.spawnParticle(EnumParticleTypes.WATER_SPLASH, x1, y1, z1, 0.0d, 0.0d, 0.0d); + Wizardry.proxy.spawnParticle(WizardryParticleType.MAGIC_BUBBLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0); + } + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_GENERIC_SWIM, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 0.5F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.MAGIC), 1.0f * modifiers.get(SpellModifiers.DAMAGE)); + // Deprecated in favour of entity riding method + //entity.addPotionEffect(new PotionEffect(Wizardry.bubblePotion, 200, 0)); + EntityBubble entitybubble = new EntityBubble(world, target.posX, target.posY, target.posZ, caster, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), false, modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(entitybubble); + target.startRiding(entitybubble); + + } + if(world.isRemote){ + + double dx = (target.posX - caster.posX)/caster.getDistanceToEntity(target); + double dy = (target.posY - caster.posY)/caster.getDistanceToEntity(target); + double dz = (target.posZ - caster.posZ)/caster.getDistanceToEntity(target); + + for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + + double x1 = caster.posX + dx*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = caster.posY + caster.getEyeHeight() - 0.4f + dy*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + dz*i/2 + world.rand.nextFloat()/5 - 0.1f; + + world.spawnParticle(EnumParticleTypes.WATER_SPLASH, x1, y1, z1, 0.0d, 0.0d, 0.0d); + Wizardry.proxy.spawnParticle(WizardryParticleType.MAGIC_BUBBLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0); + } + } + caster.swingArm(hand); + caster.playSound(SoundEvents.ENTITY_GENERIC_SWIM, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + caster.playSound(WizardrySounds.SPELL_ICE, 0.5F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/ChainLightning.java b/src/main/java/electroblob/wizardry/spell/ChainLightning.java new file mode 100644 index 00000000..1b678ba5 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/ChainLightning.java @@ -0,0 +1,141 @@ +package electroblob.wizardry.spell; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.EntityArc; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +public class ChainLightning extends Spell { + + public ChainLightning() { + super(Tier.ADVANCED, 25, Element.LIGHTNING, "chain_lightning", SpellType.ATTACK, 50, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + // First shot has range 10 (this is the only range affected by upgrades) and does 5 hearts of damage. + // Chains to up to 5 secondary targets within a range of 5 of the primary target, and then to up to 2 + // tertiary targets per secondary target within a range of 5 of that. Secondary targets are dealt 4 hearts + // of damage; tertiary targets are dealt 3 hearts of damage. + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade), 8.0f); + + // Anything can be attacked with the initial arc, because the player has control over where it goes. If they + // hit a minion or an ally, it's their problem! + if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){ + + Entity target = rayTrace.entityHit; + + if(!world.isRemote){ + EntityArc arc = new EntityArc(world); + arc.setEndpointCoords(caster.posX, caster.posY + caster.height/2, caster.posZ, + target.posX, target.posY + target.height/2, target.posZ); + world.spawnEntityInWorld(arc); + }else{ + for(int i=0;i<8;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); + } + } + + target.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); + + if(MagicDamage.isEntityImmune(DamageType.SHOCK, target)){ + if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + }else{ + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 10.0f * modifiers.get(SpellModifiers.DAMAGE)); + } + + // Secondary chaining effect + double seekerRange = 5.0d; + + List secondaryTargets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, target.posX, target.posY + target.height/2, target.posZ, world); + + for(int i=0; i tertiaryTargets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, secondaryTarget.posX, secondaryTarget.posY + secondaryTarget.height/2, secondaryTarget.posZ, world); + + for(int j=0;j targets = WizardryUtilities.getEntitiesWithinRadius(3.0d*modifiers.get(WizardryItems.blast_upgrade), (rayTrace.hitVec.xCoord+0.5), (rayTrace.hitVec.yCoord+0.5), (rayTrace.hitVec.zCoord+0.5), world); + for(int i=0;i targets = WizardryUtilities.getEntitiesWithinRadius(3.0d, target.posX, target.posY, target.posZ, world); + for(int i=0;i targets = WizardryUtilities.getEntitiesWithinRadius(5*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world, EntityPlayer.class); + + for(EntityPlayer target : targets){ + if(WizardryUtilities.isPlayerAlly(caster, target) || target == caster){ + // Damage multiplier can only ever be 1 or 1.6 for master spells, so there's little point in actually calculating this. + target.addPotionEffect(new PotionEffect(WizardryPotions.font_of_mana, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE) > 1 ? 1 : 0)); + } + } + + if(world.isRemote){ + for(int i=0;i<100*modifiers.get(WizardryItems.blast_upgrade);i++){ + double radius = (1 + world.rand.nextDouble()*4)*modifiers.get(WizardryItems.blast_upgrade); + double angle = world.rand.nextDouble()*Math.PI*2; + float hue = world.rand.nextFloat()*0.4f; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + radius*Math.cos(angle), caster.getEntityBoundingBox().minY, caster.posZ + radius*Math.sin(angle), + 0, 0.03, 0, 50, 1, 1-hue, 0.6f+hue); + + } + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/FontOfVitality.java b/src/main/java/electroblob/wizardry/spell/FontOfVitality.java new file mode 100644 index 00000000..1056b526 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/FontOfVitality.java @@ -0,0 +1,53 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class FontOfVitality extends Spell { + + public FontOfVitality() { + super(Tier.MASTER, 75, Element.HEALING, "font_of_vitality", SpellType.DEFENCE, 300, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + caster.addPotionEffect(new PotionEffect(MobEffects.ABSORPTION, (int)(1200*modifiers.get(WizardryItems.duration_upgrade)), 1, false, false)); + caster.addPotionEffect(new PotionEffect(MobEffects.REGENERATION, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 1, false, false)); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 0.6f, 0.7f); + + x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 0.8f, 0.3f); + + } + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/ForceArrow.java b/src/main/java/electroblob/wizardry/spell/ForceArrow.java new file mode 100644 index 00000000..05cd68eb --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/ForceArrow.java @@ -0,0 +1,65 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.projectile.EntityForceArrow; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class ForceArrow extends Spell { + + public ForceArrow() { + super(Tier.APPRENTICE, 15, Element.SORCERY, "force_arrow", SpellType.ATTACK, 20, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + EntityForceArrow forceArrow = new EntityForceArrow(world, caster, 1*modifiers.get(WizardryItems.range_upgrade), modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(forceArrow); + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_FORCE, 1.0f, 1.2f + world.rand.nextFloat()*0.2f); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + EntityForceArrow forceArrow = new EntityForceArrow(world, caster, target, 1*modifiers.get(WizardryItems.range_upgrade), 2, modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(forceArrow); + } + + caster.swingArm(hand); + caster.playSound(WizardrySounds.SPELL_FORCE, 1.0f, 1.2f + world.rand.nextFloat()*0.2f); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/ForceOrb.java b/src/main/java/electroblob/wizardry/spell/ForceOrb.java new file mode 100644 index 00000000..30ee52a4 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/ForceOrb.java @@ -0,0 +1,66 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.projectile.EntityForceOrb; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class ForceOrb extends Spell { + + public ForceOrb() { + super(Tier.ADVANCED, 20, Element.SORCERY, "force_orb", SpellType.ATTACK, 20, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + EntityForceOrb forceOrb = new EntityForceOrb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + world.spawnEntityInWorld(forceOrb); + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + EntityForceOrb forceOrb = new EntityForceOrb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + forceOrb.directTowards(target, 1.5f); + world.spawnEntityInWorld(forceOrb); + } + + caster.swingArm(hand); + caster.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Forcefield.java b/src/main/java/electroblob/wizardry/spell/Forcefield.java new file mode 100644 index 00000000..0b366bd1 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Forcefield.java @@ -0,0 +1,69 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.construct.EntityForcefield; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class Forcefield extends Spell { + + public Forcefield() { + super(Tier.ADVANCED, 45, Element.HEALING, "forcefield", SpellType.DEFENCE, 200, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(caster.onGround){ + if(!world.isRemote){ + EntityForcefield forcefield = new EntityForcefield(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(forcefield); + } + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION_LARGE, 1.0f, 1.0f); + return true; + } + + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + // Wizards can no longer cast forcefield when they are inside one + if(caster.onGround && world.getEntitiesWithinAABB(EntityForcefield.class, caster.getEntityBoundingBox()).isEmpty()){ + if(!world.isRemote){ + EntityForcefield forcefield = new EntityForcefield(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(forcefield); + } + caster.playSound(WizardrySounds.SPELL_CONJURATION_LARGE, 1.0f, 1.0f); + return true; + } + + return false; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/ForestsCurse.java b/src/main/java/electroblob/wizardry/spell/ForestsCurse.java new file mode 100644 index 00000000..68612acd --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/ForestsCurse.java @@ -0,0 +1,64 @@ +package electroblob.wizardry.spell; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class ForestsCurse extends Spell { + + public ForestsCurse() { + super(Tier.MASTER, 75, Element.EARTH, "forests_curse", SpellType.ATTACK, 200, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + List targets = WizardryUtilities.getEntitiesWithinRadius(5.0d*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world); + + for(EntityLivingBase target : targets){ + if(WizardryUtilities.isValidTarget(caster, target) && !MagicDamage.isEntityImmune(DamageType.POISON, target)){ + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.POISON), 4.0f * modifiers.get(SpellModifiers.DAMAGE)); + target.addPotionEffect(new PotionEffect(MobEffects.POISON, (int)(140*modifiers.get(WizardryItems.duration_upgrade)), 2)); + target.addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, (int)(140*modifiers.get(WizardryItems.duration_upgrade)), 2)); + target.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, (int)(140*modifiers.get(WizardryItems.duration_upgrade)), 2)); + } + } + + if(world.isRemote){ + for(int i=0;i<50*modifiers.get(WizardryItems.blast_upgrade);i++){ + double radius = (1 + world.rand.nextDouble()*4)*modifiers.get(WizardryItems.blast_upgrade); + double angle = world.rand.nextDouble()*Math.PI*2; + float brightness = world.rand.nextFloat()/4; + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, caster.posX + radius*Math.cos(angle), WizardryUtilities.getPlayerEyesPos(caster) + 0.5, caster.posZ + radius*Math.sin(angle), + 0, -0.2, 0, 0, 0.05f+brightness, 0.2f+brightness, 0.0f); + brightness = world.rand.nextFloat()/4; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + radius*Math.cos(angle), WizardryUtilities.getPlayerEyesPos(caster) + 0.5, caster.posZ + radius*Math.sin(angle), + 0, -0.05, 0, 50, 0.1f+brightness, 0.2f+brightness, 0.0f); + Wizardry.proxy.spawnParticle(WizardryParticleType.LEAF, world, caster.posX + radius*Math.cos(angle), WizardryUtilities.getPlayerEyesPos(caster) + 0.5, caster.posZ + radius*Math.sin(angle), 0, -0.01, 0, 40 + world.rand.nextInt(12)); + + } + } + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Freeze.java b/src/main/java/electroblob/wizardry/spell/Freeze.java new file mode 100644 index 00000000..01b289cc --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Freeze.java @@ -0,0 +1,158 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntityBlazeMinion; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityBlaze; +import net.minecraft.entity.monster.EntityMagmaCube; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +public class Freeze extends Spell { + + public Freeze() { + super(Tier.BASIC, 5, Element.ICE, "freeze", SpellType.ATTACK, 10, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + // Entity ray trace is done first because block ray trace passes through entities; if it was the other + // way round, entities would only be hit when there were no blocks in range behind them. + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + + EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; + + if(target instanceof EntityBlaze || target instanceof EntityMagmaCube || target instanceof EntityBlazeMinion){ + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.FROST), 3.0f * modifiers.get(SpellModifiers.DAMAGE)); + } + + if(MagicDamage.isEntityImmune(DamageType.FROST, target)){ + if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + }else{ + target.addPotionEffect(new PotionEffect(WizardryPotions.frost, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 1)); + } + + if(target.isBurning()){ + target.extinguish(); + } + + if(world.isRemote){ + double dx = target.posX - caster.posX; + double dy = (target.getEntityBoundingBox().minY + target.height/2) - WizardryUtilities.getPlayerEyesPos(caster); + double dz = target.posZ - caster.posZ; + for(int i=1;i<5;i++){ + float brightness = 0.5f + (world.rand.nextFloat()/2); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); + Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, -0.02, 0, 40 + world.rand.nextInt(10)); + } + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); + return true; + + }else{ + rayTrace = WizardryUtilities.rayTrace(10*modifiers.get(WizardryItems.range_upgrade), world, caster, true); + // Gets block the player is looking at and sets to ice or covers with snow as necessary + // Note how the block is set on the server side only (kinda obvious really) but the particles are + // spawned on the client side only. + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + + BlockPos pos = rayTrace.getBlockPos(); + + if(world.getBlockState(pos).getBlock() == Blocks.WATER && !world.isRemote){ + world.setBlockState(pos, Blocks.ICE.getDefaultState()); + }else if(world.getBlockState(pos).getBlock() == Blocks.LAVA && !world.isRemote){ + world.setBlockState(pos, Blocks.OBSIDIAN.getDefaultState()); + }else if(world.getBlockState(pos).getBlock() == Blocks.FLOWING_LAVA && !world.isRemote){ + world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState()); + }else if(rayTrace.sideHit == EnumFacing.UP && !world.isRemote && world.isSideSolid(pos, EnumFacing.UP) && WizardryUtilities.canBlockBeReplaced(world, pos.up())){ + world.setBlockState(pos.up(), Blocks.SNOW_LAYER.getDefaultState()); + } + + if(world.isRemote){ + + double dx = pos.getX() + 0.5 - caster.posX; + double dy = pos.getY() + 0.5 - WizardryUtilities.getPlayerEyesPos(caster); + double dz = pos.getZ() + 0.5 - caster.posZ; + + for(int i=1;i<5;i++){ + float brightness = 0.5f + (world.rand.nextFloat()/2); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0.0d, 0.0d, 0.0d, 20 + world.rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 20 + world.rand.nextInt(8), 1.0f, 1.0f, 1.0f); + } + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); + return true; + } + } + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(target instanceof EntityBlaze || target instanceof EntityMagmaCube || target instanceof EntityBlazeMinion){ + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.FROST), 3.0f * modifiers.get(SpellModifiers.DAMAGE)); + } + + if(!world.isRemote && !MagicDamage.isEntityImmune(DamageType.FROST, target)){ + target.addPotionEffect(new PotionEffect(WizardryPotions.frost, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 1)); + } + + if(target.isBurning()){ + target.extinguish(); + } + + if(world.isRemote){ + double dx = target.posX - caster.posX; + double dy = (target.getEntityBoundingBox().minY + target.height/2) - (caster.posY + caster.getEyeHeight()); + double dz = target.posZ - caster.posZ; + for(int i=1;i<5;i++){ + float brightness = 0.5f + (world.rand.nextFloat()/2); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + caster.getEyeHeight() + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); + Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + caster.getEyeHeight() + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, -0.02, 0, 40 + world.rand.nextInt(10)); + } + } + + caster.swingArm(hand); + caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/FreezingWeapon.java b/src/main/java/electroblob/wizardry/spell/FreezingWeapon.java new file mode 100644 index 00000000..911be3f4 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/FreezingWeapon.java @@ -0,0 +1,71 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryEnchantments; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.item.ItemBow; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemSword; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class FreezingWeapon extends Spell { + + /** The NBT tag name for storing the level of frost enchantment in the arrow's tag compound. (There's nothing + * stopping you from using this elsewhere to shoot freezing arrows if you want to...) */ + public static final String FREEZING_ARROW_NBT_KEY = "frostLevel"; + + public FreezingWeapon() { + super(Tier.ADVANCED, 35, Element.ICE, "freezing_weapon", SpellType.UTILITY, 70, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + // Won't work if the weapon already has the enchantment + if(WizardData.get(caster) != null && WizardData.get(caster).getImbuementDuration(WizardryEnchantments.freezing_weapon) <= 0){ + + for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(caster)){ + + if(stack != null){ + + if((stack.getItem() instanceof ItemSword || stack.getItem() instanceof ItemBow) && !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.freezing_weapon)){ + // The enchantment level as determined by the damage multiplier. The + 0.5f is so that + // weird float processing doesn't incorrectly round it down. + stack.addEnchantment(WizardryEnchantments.freezing_weapon, modifiers.get(SpellModifiers.DAMAGE) == 1.0f ? 1 : (int)((modifiers.get(SpellModifiers.DAMAGE) - 1.0f)/Constants.DAMAGE_INCREASE_PER_TIER + 0.5f)); + + WizardData.get(caster).setImbuementDuration(WizardryEnchantments.freezing_weapon, (int)(900*modifiers.get(WizardryItems.duration_upgrade))); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.9f, 0.7f, 1.0f); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); + return true; + + } + } + } + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/FrostAxe.java b/src/main/java/electroblob/wizardry/spell/FrostAxe.java new file mode 100644 index 00000000..1d48473e --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/FrostAxe.java @@ -0,0 +1,50 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.item.IConjuredItem; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class FrostAxe extends Spell { + + public FrostAxe() { + super(Tier.ADVANCED, 45, Element.ICE, "frost_axe", SpellType.UTILITY, 50, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + ItemStack frostaxe = new ItemStack(WizardryItems.frost_axe); + + IConjuredItem.setDurationMultiplier(frostaxe, modifiers.get(WizardryItems.duration_upgrade)); + + if(!WizardryUtilities.doesPlayerHaveItem(caster, WizardryItems.frost_axe) && ConjureBow.conjureItemInInventory(caster, frostaxe)){ + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, world, x1, y1, z1, 0, -0.02d, 0, 40 + world.rand.nextInt(10)); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0f, 1.0f); + return true; + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/FrostRay.java b/src/main/java/electroblob/wizardry/spell/FrostRay.java new file mode 100644 index 00000000..10897960 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/FrostRay.java @@ -0,0 +1,132 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityBlaze; +import net.minecraft.entity.monster.EntityMagmaCube; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +public class FrostRay extends Spell { + + public FrostRay() { + super(Tier.APPRENTICE, 5, Element.ICE, "frost_ray", SpellType.ATTACK, 0, EnumAction.NONE, true); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + Vec3d look = caster.getLookVec(); + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + + EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; + + if(target.isBurning()){ + target.extinguish(); + } + + if(MagicDamage.isEntityImmune(DamageType.FROST, target)){ + if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + }else{ + // For frost ray the entity can move slightly, unlike freeze. + target.addPotionEffect(new PotionEffect(WizardryPotions.frost, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 0)); + + float baseDamage = target instanceof EntityBlaze || target instanceof EntityMagmaCube ? 6.0f : 3.0f; + WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeDirectMagicDamage(caster, DamageType.FROST), baseDamage * modifiers.get(SpellModifiers.DAMAGE)); + + } + } + + if(world.isRemote){ + for(int i=0; i<20; i++){ + double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, look.xCoord*modifiers.get(WizardryItems.range_upgrade), look.yCoord*modifiers.get(WizardryItems.range_upgrade), look.zCoord*modifiers.get(WizardryItems.range_upgrade), 8 + world.rand.nextInt(12), 0.4f, 0.6f, 1.0f); + + x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, look.xCoord*modifiers.get(WizardryItems.range_upgrade), look.yCoord*modifiers.get(WizardryItems.range_upgrade), look.zCoord*modifiers.get(WizardryItems.range_upgrade), 8 + world.rand.nextInt(12), 1.0f, 1.0f, 1.0f); + } + } + + if(ticksInUse % 12 == 0){ + if(ticksInUse == 0) WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 0.5F, 1.0f); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_ICE, 0.5F, 1.0f); + } + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + + if(target != null){ + + Vec3d vec = new Vec3d(target.posX - caster.posX, target.posY - caster.posY, target.posZ - caster.posZ).normalize(); + + if(target.isBurning()){ + target.extinguish(); + } + + if(!MagicDamage.isEntityImmune(DamageType.FROST, target)){ + // For frost ray the entity can move slightly, unlike freeze. + target.addPotionEffect(new PotionEffect(WizardryPotions.frost, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 0)); + + float baseDamage = target instanceof EntityBlaze || target instanceof EntityMagmaCube ? 6.0f : 3.0f; + WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeDirectMagicDamage(caster, DamageType.FROST), baseDamage * modifiers.get(SpellModifiers.DAMAGE)); + + } + + if(world.isRemote){ + for(int i=0; i<20; i++){ + double x1 = caster.posX + vec.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = caster.posY + caster.getEyeHeight() - 0.4f + vec.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + vec.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, vec.xCoord*modifiers.get(WizardryItems.range_upgrade), vec.yCoord*modifiers.get(WizardryItems.range_upgrade), vec.zCoord*modifiers.get(WizardryItems.range_upgrade), 8 + world.rand.nextInt(12), 0.4f, 0.6f, 1.0f); + + x1 = caster.posX + vec.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + y1 = caster.posY + caster.getEyeHeight() - 0.4f + vec.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + z1 = caster.posZ + vec.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, vec.xCoord*modifiers.get(WizardryItems.range_upgrade), vec.yCoord*modifiers.get(WizardryItems.range_upgrade), vec.zCoord*modifiers.get(WizardryItems.range_upgrade), 8 + world.rand.nextInt(12), 1.0f, 1.0f, 1.0f); + } + } + + if(ticksInUse % 12 == 0){ + if(ticksInUse == 0) caster.playSound(WizardrySounds.SPELL_ICE, 0.5F, 1.0f); + caster.playSound(WizardrySounds.SPELL_LOOP_ICE, 0.5F, 1.0f); + } + + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs() { + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/FrostSigil.java b/src/main/java/electroblob/wizardry/spell/FrostSigil.java new file mode 100644 index 00000000..5423e9e8 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/FrostSigil.java @@ -0,0 +1,50 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.construct.EntityFrostSigil; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class FrostSigil extends Spell { + + public FrostSigil() { + super(Tier.APPRENTICE, 10, Element.ICE, "frost_sigil", SpellType.ATTACK, 20, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.rayTrace(10*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK && rayTrace.sideHit == EnumFacing.UP){ + if(!world.isRemote){ + double x = rayTrace.hitVec.xCoord; + double y = rayTrace.hitVec.yCoord; + double z = rayTrace.hitVec.zCoord; + EntityFrostSigil frostsigil = new EntityFrostSigil(world, x, y, z, caster, modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(frostsigil); + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + return true; + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Glide.java b/src/main/java/electroblob/wizardry/spell/Glide.java new file mode 100644 index 00000000..45fa88d7 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Glide.java @@ -0,0 +1,46 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class Glide extends Spell { + + public Glide() { + super(Tier.ADVANCED, 5, Element.EARTH, "glide", SpellType.UTILITY, 0, EnumAction.NONE, true); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(caster.motionY < -0.1 && !caster.isInWater()){ + caster.motionY = -0.1; + if(Math.abs(caster.motionX) < 0.4 && Math.abs(caster.motionZ) < 0.4){ + caster.addVelocity(caster.getLookVec().xCoord/8, 0, caster.getLookVec().zCoord/8); + //entityplayer.moveEntity(entityplayer.motionX*10, 0, entityplayer.motionZ*10); + } + caster.fallDistance = 0.0f; + } + + if(world.isRemote){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX - 0.25d + world.rand.nextDouble()/2, WizardryUtilities.getPlayerEyesPos(caster) - 1.5f + world.rand.nextDouble(), caster.posZ - 0.25d + world.rand.nextDouble()/2, 0, -0.1F, 0, 15, 1.0f, 1.0f, 1.0f); + Wizardry.proxy.spawnParticle(WizardryParticleType.LEAF, world, caster.posX - 0.25d + world.rand.nextDouble()/2, WizardryUtilities.getPlayerEyesPos(caster) - 1.5f + world.rand.nextDouble(), caster.posZ - 0.25d + world.rand.nextDouble()/2, 0, -0.03, 0, 20); + } + + if(ticksInUse % 24 == 0){ + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ITEM_ELYTRA_FLYING, 0.5F, 1.0f); + } + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/GreaterFireball.java b/src/main/java/electroblob/wizardry/spell/GreaterFireball.java new file mode 100644 index 00000000..93414391 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/GreaterFireball.java @@ -0,0 +1,86 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.projectile.EntityLargeFireball; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class GreaterFireball extends Spell { + + public GreaterFireball() { + super(Tier.ADVANCED, 20, Element.FIRE, "greater_fireball", SpellType.ATTACK, 30, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + Vec3d look = caster.getLookVec(); + + if(!world.isRemote){ + EntityLargeFireball fireball = new EntityLargeFireball(world, caster, 1, 1, 1); + fireball.setPosition( + caster.posX + look.xCoord, + caster.posY + look.yCoord + 1.3, + caster.posZ + look.zCoord); + fireball.accelerationX = look.xCoord * 0.1; + fireball.accelerationY = look.yCoord * 0.1; + fireball.accelerationZ = look.zCoord * 0.1; + world.spawnEntityInWorld(fireball); + } + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1); + caster.swingArm(hand); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + + EntityLargeFireball fireball = new EntityLargeFireball(world, caster, 1, 1, 1); + + double dx = target.posX - caster.posX; + double dy = target.getEntityBoundingBox().minY + (double)(target.height / 2.0F) - (caster.posY + (double)(caster.height / 2.0F)); + double dz = target.posZ - caster.posZ; + + fireball.accelerationX = dx/caster.getDistanceToEntity(target) * 0.1; + fireball.accelerationY = dy/caster.getDistanceToEntity(target) * 0.1; + fireball.accelerationZ = dz/caster.getDistanceToEntity(target) * 0.1; + + fireball.setPosition(caster.posX, caster.posY + caster.getEyeHeight(), caster.posZ); + + world.spawnEntityInWorld(fireball); + } + + caster.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1); + caster.swingArm(hand); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/GreaterHeal.java b/src/main/java/electroblob/wizardry/spell/GreaterHeal.java new file mode 100644 index 00000000..df20a7dc --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/GreaterHeal.java @@ -0,0 +1,63 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class GreaterHeal extends Spell { + + public GreaterHeal() { + super(Tier.ADVANCED, 15, Element.HEALING, "greater_heal", SpellType.DEFENCE, 40, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(caster.shouldHeal()){ + caster.heal((int)(8*modifiers.get(SpellModifiers.DAMAGE))); + if(world.isRemote){ + for(int i=0; i<10; i++){ + double dx = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double dy = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double dz = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, dx, dy, dz, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); + } + } + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(caster.getHealth() < caster.getMaxHealth()){ + caster.heal((int)(8*modifiers.get(SpellModifiers.DAMAGE))); + if(world.isRemote){ + for(int i=0; i<10; i++){ + double dx = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double dy = (double)((float)caster.posY + caster.getEyeHeight() - 0.5F + world.rand.nextFloat()); + double dz = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, dx, dy, dz, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); + } + } + caster.playSound(WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/GroupHeal.java b/src/main/java/electroblob/wizardry/spell/GroupHeal.java new file mode 100644 index 00000000..939ef998 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/GroupHeal.java @@ -0,0 +1,89 @@ +package electroblob.wizardry.spell; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.ISummonedCreature; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class GroupHeal extends Spell { + + public GroupHeal() { + super(Tier.ADVANCED, 35, Element.HEALING, "group_heal", SpellType.DEFENCE, 150, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + boolean flag = false; + + List targets = WizardryUtilities.getEntitiesWithinRadius(5*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world); + + for(EntityLivingBase target : targets){ + + if(target instanceof EntityPlayer){ + + if(WizardryUtilities.isPlayerAlly(caster, (EntityPlayer)target) || target == caster){ + + if(((EntityPlayer)target).shouldHeal()){ + + target.heal((int)(6*modifiers.get(SpellModifiers.DAMAGE))); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double d0 = (double)((float)target.posX + world.rand.nextFloat()*2 - 1.0F); + double d1 = (double)((float)WizardryUtilities.getPlayerEyesPos((EntityPlayer)target) - 0.5F + world.rand.nextFloat()); + double d2 = (double)((float)target.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, d0, d1, d2, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + flag = true; + } + } + + // Now also works on summoned creatures + }else if(target instanceof ISummonedCreature){ + + EntityLivingBase summoner = ((ISummonedCreature)target).getCaster(); + + if(summoner == caster || (summoner instanceof EntityPlayer && WizardryUtilities.isPlayerAlly(caster, (EntityPlayer)summoner))){ + + if(target.getHealth() < target.getMaxHealth()){ + + target.heal((int)(6*modifiers.get(SpellModifiers.DAMAGE))); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double d0 = (double)((float)target.posX + world.rand.nextFloat()*2 - 1.0F); + double d1 = (double)((float)WizardryUtilities.getPlayerEyesPos((EntityPlayer)target) - 0.5F + world.rand.nextFloat()); + double d2 = (double)((float)target.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, d0, d1, d2, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + flag = true; + } + } + } + } + + return flag; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/GrowthAura.java b/src/main/java/electroblob/wizardry/spell/GrowthAura.java new file mode 100644 index 00000000..7e52855e --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/GrowthAura.java @@ -0,0 +1,75 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.block.IGrowable; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.item.ItemDye; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class GrowthAura extends Spell { + + public GrowthAura() { + super(Tier.APPRENTICE, 20, Element.EARTH, "growth_aura", SpellType.UTILITY, 50, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + boolean flag = false; + + for(int i=-2; i<1; i++){ + + for(int j=-1; j<2; j++){ + + int x = (int)caster.posX + i; + int y = WizardryUtilities.getNearestFloorLevelC(world, new BlockPos(caster.posX + i, caster.posY, caster.posZ + j), 2) - 1; + int z = (int)caster.posZ + j; + + BlockPos pos = new BlockPos(x, y, z); + + if(y > -1 && caster.getDistance(x, y, z) <= 2){ + + IBlockState state = world.getBlockState(pos); + + if(state.getBlock() instanceof IGrowable){ + + IGrowable igrowable = (IGrowable)state.getBlock(); + + if(igrowable.canGrow(world, pos, state, world.isRemote)){ + + if(!world.isRemote){ + if(igrowable.canUseBonemeal(world, world.rand, pos, state)){ + igrowable.grow(world, world.rand, pos, state); + } + }else{ + // Yes, it's meant to be 0, and it automatically changes it to 15. + ItemDye.spawnBonemealParticles(world, pos, 0); + } + + flag = true; + } + } + } + } + } + + if(flag) WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return flag; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Hailstorm.java b/src/main/java/electroblob/wizardry/spell/Hailstorm.java new file mode 100644 index 00000000..7dc8ee73 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Hailstorm.java @@ -0,0 +1,57 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.construct.EntityHailstorm; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class Hailstorm extends Spell { + + public Hailstorm() { + super(Tier.MASTER, 75, Element.ICE, "hailstorm", SpellType.ATTACK, 300, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.rayTrace(20*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(!world.isRemote){ + double x = rayTrace.hitVec.xCoord; + double y = rayTrace.hitVec.yCoord; + double z = rayTrace.hitVec.zCoord; + // Moves the entity back towards the caster a bit, so the area of effect is better centred on the position. + // 3.0d is the distance to move the entity back towards the caster. + double dx = caster.posX - x; + double dz = caster.posZ - z; + double distRatio = 3.0d/Math.sqrt(dx*dx + dz*dz); + x += dx*distRatio; + z += dz*distRatio; + + EntityHailstorm hailstorm = new EntityHailstorm(world, x, y + 5, z, caster, (int)(120*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + hailstorm.rotationYaw = caster.rotationYawHead; + world.spawnEntityInWorld(hailstorm); + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + return true; + } + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Heal.java b/src/main/java/electroblob/wizardry/spell/Heal.java new file mode 100644 index 00000000..8a7044e3 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Heal.java @@ -0,0 +1,67 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class Heal extends Spell { + + public Heal() { + super(Tier.BASIC, 5, Element.HEALING, "heal", SpellType.DEFENCE, 20, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(caster.shouldHeal()){ + + caster.heal((int)(4*modifiers.get(SpellModifiers.DAMAGE))); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double d0 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + // Apparently the client side spawns the particles 1 block higher than it should... hence the - 0.5F. + double d1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double d2 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, d0, d1, d2, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(caster.getHealth() < caster.getMaxHealth()){ + caster.heal((int)(4*modifiers.get(SpellModifiers.DAMAGE))); + if(world.isRemote){ + for(int i=0; i<10; i++){ + double dx = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double dy = (double)((float)caster.posY + caster.getEyeHeight() - 0.5F + world.rand.nextFloat()); + double dz = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, dx, dy, dz, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); + } + } + caster.playSound(WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/HealAlly.java b/src/main/java/electroblob/wizardry/spell/HealAlly.java new file mode 100644 index 00000000..3ab49587 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/HealAlly.java @@ -0,0 +1,54 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class HealAlly extends Spell { + + public HealAlly() { + super(Tier.APPRENTICE, 10, Element.HEALING, "heal_ally", SpellType.DEFENCE, 20, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade), 8.0f); + + if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){ + EntityLivingBase target = (EntityLivingBase)rayTrace.entityHit; + if(target.getHealth() < target.getMaxHealth()){ + target.heal((int)(5*modifiers.get(SpellModifiers.DAMAGE))); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double d0 = (double)((float)target.posX + world.rand.nextFloat()*2 - 1.0F); + // Apparently the client side spawns the particles 1 block higher than it should... hence the - 0.5F. + double d1 = (double)((float)target.getEntityBoundingBox().minY + target.height - 0.5f + world.rand.nextFloat()); + double d2 = (double)((float)target.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, d0, d1, d2, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); + } + } + + caster.swingArm(hand); + target.playSound(WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/HealingAura.java b/src/main/java/electroblob/wizardry/spell/HealingAura.java new file mode 100644 index 00000000..bcd6ea61 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/HealingAura.java @@ -0,0 +1,62 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.construct.EntityHealAura; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class HealingAura extends Spell { + + public HealingAura() { + super(Tier.ADVANCED, 35, Element.HEALING, "healing_aura", SpellType.DEFENCE, 150, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(caster.onGround){ + if(!world.isRemote){ + EntityHealAura healaura = new EntityHealAura(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(healaura); + } + return true; + } + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + + if(target != null){ + if(caster.onGround && world.getEntitiesWithinAABB(EntityHealAura.class, caster.getEntityBoundingBox()).isEmpty()){ + if(!world.isRemote){ + EntityHealAura healaura = new EntityHealAura(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(healaura); + } + return true; + } + return false; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs() { + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/HomingSpark.java b/src/main/java/electroblob/wizardry/spell/HomingSpark.java new file mode 100644 index 00000000..65ed1d64 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/HomingSpark.java @@ -0,0 +1,63 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.projectile.EntitySpark; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class HomingSpark extends Spell { + + public HomingSpark() { + super(Tier.APPRENTICE, 10, Element.LIGHTNING, "homing_spark", SpellType.ATTACK, 20, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + EntitySpark spark = new EntitySpark(world, caster, modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(spark); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + } + caster.swingArm(hand); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + EntitySpark spark = new EntitySpark(world, caster, modifiers.get(SpellModifiers.DAMAGE)); + spark.directTowards(target, 0.5f); + world.spawnEntityInWorld(spark); + caster.playSound(WizardrySounds.SPELL_CONJURATION, 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + } + caster.swingArm(hand); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/IceAge.java b/src/main/java/electroblob/wizardry/spell/IceAge.java new file mode 100644 index 00000000..8f57ee59 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/IceAge.java @@ -0,0 +1,147 @@ +package electroblob.wizardry.spell; + +import java.util.List; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryAchievements; +import electroblob.wizardry.registry.WizardryBlocks; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.tileentity.TileEntityStatue; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityBlaze; +import net.minecraft.entity.monster.EntityMagmaCube; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class IceAge extends Spell { + + private static final int baseDuration = 1200; + + public IceAge() { + super(Tier.MASTER, 70, Element.ICE, "ice_age", SpellType.ATTACK, 250, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + List targets = WizardryUtilities.getEntitiesWithinRadius(7*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world); + + for(EntityLivingBase target : targets){ + if(WizardryUtilities.isValidTarget(caster, target)){ + if(!world.isRemote){ + if(target instanceof EntityBlaze || target instanceof EntityMagmaCube){ + // These have been removed for the time being because they cause the entity to sink into the floor when it breaks out. + //target.attackEntityFrom(WizardryUtilities.causePlayerMagicDamage(entityplayer), 8.0f * modifiers.get(SpellModifiers.DAMAGE)); + }else{ + //target.attackEntityFrom(WizardryUtilities.causePlayerMagicDamage(entityplayer), 4.0f * modifiers.get(SpellModifiers.DAMAGE)); + } + if(target.isBurning()){ + target.extinguish(); + } + + if(target instanceof EntityBlaze) caster.addStat(WizardryAchievements.freeze_blaze, 1); + + if(target instanceof EntityLiving){ + + // Stops the entity looking red while frozen and the resulting z-fighting + target.hurtTime = 0; + + BlockPos pos = new BlockPos(target); + + // Short mobs such as spiders and pigs + if((target.height < 1.2 || target.isChild()) && WizardryUtilities.canBlockBeReplaced(world, pos)){ + world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); + if(world.getTileEntity(pos) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart((EntityLiving) target, 1, 1); + ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + } + target.setDead(); + target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + } + // Normal sized mobs like zombies and skeletons + else if(target.height < 2.5 && WizardryUtilities.canBlockBeReplaced(world, pos) && WizardryUtilities.canBlockBeReplaced(world, pos.up())){ + world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); + if(world.getTileEntity(pos) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart((EntityLiving) target, 1, 2); + ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + } + + world.setBlockState(pos.up(), WizardryBlocks.ice_statue.getDefaultState()); + if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart((EntityLiving) target, 2, 2); + } + target.setDead(); + target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + } + // Tall mobs like endermen + else if(WizardryUtilities.canBlockBeReplaced(world, pos) && WizardryUtilities.canBlockBeReplaced(world, pos.up()) && WizardryUtilities.canBlockBeReplaced(world, pos.up(2))){ + world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); + if(world.getTileEntity(pos) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart((EntityLiving) target, 1, 3); + ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + } + + world.setBlockState(pos.up(), WizardryBlocks.ice_statue.getDefaultState()); + if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart((EntityLiving) target, 2, 3); + } + + world.setBlockState(pos.up(2), WizardryBlocks.ice_statue.getDefaultState()); + if(world.getTileEntity(pos.up(2)) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos.up(2))).setCreatureAndPart((EntityLiving) target, 3, 3); + } + target.setDead(); + target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + } + } + } + } + } + if(!world.isRemote){ + for(int i=-7; i<8; i++){ + for(int j=-7; j<8; j++){ + + BlockPos pos = new BlockPos(caster).add(i, 0, j); + + int y = WizardryUtilities.getNearestFloorLevelB(world, new BlockPos(pos), 7); + + pos = new BlockPos(pos.getX(), y, pos.getZ()); + + double dist = caster.getDistance((int)caster.posX + i, y, (int)caster.posZ + j); + + // Randomised with weighting so that the nearer the block the more likely it is to be snowed. + if(y != -1 && world.rand.nextInt((int)dist*2 + 1) < 7 && dist < 8){ + if(world.getBlockState(pos.down()) == Blocks.WATER.getDefaultState()){ + world.setBlockState(pos.down(), Blocks.ICE.getDefaultState()); + }else if(world.getBlockState(pos.down()) == Blocks.LAVA.getDefaultState()){ + world.setBlockState(pos.down(), Blocks.OBSIDIAN.getDefaultState()); + }else if(world.getBlockState(pos.down()) == Blocks.FLOWING_LAVA.getDefaultState()){ + world.setBlockState(pos.down(), Blocks.COBBLESTONE.getDefaultState()); + }else{ + world.setBlockState(pos, Blocks.SNOW_LAYER.getDefaultState()); + } + } + } + } + } + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 0.7F, 1.0f); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_WIND, 1.0F, 1.0f); + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/IceCharge.java b/src/main/java/electroblob/wizardry/spell/IceCharge.java new file mode 100644 index 00000000..c419b374 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/IceCharge.java @@ -0,0 +1,66 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.projectile.EntityIceCharge; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class IceCharge extends Spell { + + public IceCharge() { + super(Tier.ADVANCED, 20, Element.ICE, "ice_charge", SpellType.ATTACK, 30, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + EntityIceCharge icecharge = new EntityIceCharge(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + world.spawnEntityInWorld(icecharge); + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + EntityIceCharge icecharge = new EntityIceCharge(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + icecharge.directTowards(target, 1.5f); + world.spawnEntityInWorld(icecharge); + } + + caster.swingArm(hand); + caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/IceLance.java b/src/main/java/electroblob/wizardry/spell/IceLance.java new file mode 100644 index 00000000..dcef7c07 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/IceLance.java @@ -0,0 +1,63 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.projectile.EntityIceLance; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class IceLance extends Spell { + + public IceLance() { + super(Tier.ADVANCED, 20, Element.ICE, "ice_lance", SpellType.ATTACK, 20, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + EntityIceLance iceLance = new EntityIceLance(world, caster, 2*modifiers.get(WizardryItems.range_upgrade), modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(iceLance); + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + EntityIceLance iceLance = new EntityIceLance(world, caster, target, 2*modifiers.get(WizardryItems.range_upgrade), 4, modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(iceLance); + } + caster.swingArm(hand); + caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/IceShard.java b/src/main/java/electroblob/wizardry/spell/IceShard.java new file mode 100644 index 00000000..ddda2a1d --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/IceShard.java @@ -0,0 +1,63 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.projectile.EntityIceShard; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class IceShard extends Spell { + + public IceShard() { + super(Tier.APPRENTICE, 10, Element.ICE, "ice_shard", SpellType.ATTACK, 10, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + EntityIceShard iceShard = new EntityIceShard(world, caster, 2*modifiers.get(WizardryItems.range_upgrade), modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(iceShard); + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + EntityIceShard iceShard = new EntityIceShard(world, caster, target, 2*modifiers.get(WizardryItems.range_upgrade), 4, modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(iceShard); + } + caster.swingArm(hand); + caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/IceShroud.java b/src/main/java/electroblob/wizardry/spell/IceShroud.java new file mode 100644 index 00000000..85843dc1 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/IceShroud.java @@ -0,0 +1,67 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class IceShroud extends Spell { + + public IceShroud() { + super(Tier.ADVANCED, 40, Element.ICE, "ice_shroud", SpellType.DEFENCE, 250, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + // Cannot be cast when it has already been cast + if(!caster.isPotionActive(WizardryPotions.ice_shroud)){ + if(!world.isRemote){ + caster.addPotionEffect(new PotionEffect(WizardryPotions.ice_shroud, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0)); + } + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); + return true; + } + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + + if(target != null){ + // Cannot be cast when it has already been cast + if(!caster.isPotionActive(WizardryPotions.ice_shroud)){ + if(!world.isRemote){ + caster.addPotionEffect(new PotionEffect(WizardryPotions.ice_shroud, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0)); + } + caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); + return true; + } + return false; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs() { + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/IceSpikes.java b/src/main/java/electroblob/wizardry/spell/IceSpikes.java new file mode 100644 index 00000000..95325e0f --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/IceSpikes.java @@ -0,0 +1,108 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.construct.EntityIceSpike; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class IceSpikes extends Spell { + + public IceSpikes() { + super(Tier.ADVANCED, 30, Element.ICE, "ice_spikes", SpellType.ATTACK, 75, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.rayTrace(20*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK && rayTrace.sideHit == EnumFacing.UP){ + + if(!world.isRemote){ + + double x = rayTrace.hitVec.xCoord; + double y = rayTrace.hitVec.yCoord; + double z = rayTrace.hitVec.zCoord; + + for(int i=0; i<(int)(18*modifiers.get(WizardryItems.blast_upgrade)); i++){ + + float angle = (float)(world.rand.nextFloat()*Math.PI*2); + double radius = 0.5 + world.rand.nextDouble()*2*modifiers.get(WizardryItems.blast_upgrade); + + double x1 = x + radius*MathHelper.sin(angle); + double z1 = z + radius*MathHelper.cos(angle); + double y1 = WizardryUtilities.getNearestFloorLevel(world, new BlockPos(MathHelper.floor_double(x1), (int)y, MathHelper.floor_double(z1)), 2) - 1; + + if(y1 > -1){ + EntityIceSpike icespike = new EntityIceSpike(world, x1, y1, z1, caster, 30 + world.rand.nextInt(15), modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(icespike); + } + } + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + return true; + } + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + + double x = target.posX; + double y = target.posY; + double z = target.posZ; + + for(int i=0; i<(int)(18*modifiers.get(WizardryItems.blast_upgrade)); i++){ + + float angle = (float)(world.rand.nextFloat()*Math.PI*2); + double radius = 0.5 + world.rand.nextDouble()*2*modifiers.get(WizardryItems.blast_upgrade); + + double x1 = x + radius*MathHelper.sin(angle); + double z1 = z + radius*MathHelper.cos(angle); + double y1 = WizardryUtilities.getNearestFloorLevel(world, new BlockPos(MathHelper.floor_double(x1), (int)y, MathHelper.floor_double(z1)), 2) - 1; + + if(y1 > -1){ + EntityIceSpike icespike = new EntityIceSpike(world, x1, y1, z1, caster, 30 + world.rand.nextInt(15), modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(icespike); + } + } + } + caster.swingArm(hand); + caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/IceStatue.java b/src/main/java/electroblob/wizardry/spell/IceStatue.java new file mode 100644 index 00000000..1928421a --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/IceStatue.java @@ -0,0 +1,120 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryAchievements; +import electroblob.wizardry.registry.WizardryBlocks; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.tileentity.TileEntityStatue; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.monster.EntityBlaze; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class IceStatue extends Spell { + + private static final int baseDuration = 400; + + public IceStatue() { + super(Tier.APPRENTICE, 15, Element.ICE, "ice_statue", SpellType.ATTACK, 40, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + Vec3d look = caster.getLookVec(); + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLiving && !world.isRemote){ + + EntityLiving target = (EntityLiving) rayTrace.entityHit; + + BlockPos pos = new BlockPos(target); + + if(target.isBurning()){ + target.extinguish(); + } + + // Stops the entity looking red while frozen and the resulting z-fighting + target.hurtTime = 0; + + if(target instanceof EntityBlaze) caster.addStat(WizardryAchievements.freeze_blaze, 1); + + // Short mobs such as spiders and pigs + if((target.height < 1.2 || target.isChild()) && WizardryUtilities.canBlockBeReplaced(world, pos)){ + world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); + if(world.getTileEntity(pos) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 1); + ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + } + target.setDead(); + target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + } + // Normal sized mobs like zombies and skeletons + else if(target.height < 2.5 && WizardryUtilities.canBlockBeReplaced(world, pos) && WizardryUtilities.canBlockBeReplaced(world, pos.up())){ + world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); + if(world.getTileEntity(pos) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 2); + ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + } + + world.setBlockState(pos.up(), WizardryBlocks.ice_statue.getDefaultState()); + if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(target, 2, 2); + } + target.setDead(); + target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + } + // Tall mobs like endermen and iron golems + else if(WizardryUtilities.canBlockBeReplaced(world, pos) && WizardryUtilities.canBlockBeReplaced(world, pos.up()) && WizardryUtilities.canBlockBeReplaced(world, pos.up(2))){ + world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); + if(world.getTileEntity(pos) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 3); + ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + } + + world.setBlockState(pos.up(), WizardryBlocks.ice_statue.getDefaultState()); + if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(target, 2, 3); + } + + world.setBlockState(pos.up(2), WizardryBlocks.ice_statue.getDefaultState()); + if(world.getTileEntity(pos.up(2)) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos.up(2))).setCreatureAndPart(target, 3, 3); + } + target.setDead(); + target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + } + } + if(world.isRemote){ + for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + float brightness = 0.5f + (world.rand.nextFloat()/2); + + double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); + Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, world, x1, y1, z1, 0.0d, -0.02d, 0.0d, 20 + world.rand.nextInt(10)); + + } + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Ignite.java b/src/main/java/electroblob/wizardry/spell/Ignite.java new file mode 100644 index 00000000..2807a570 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Ignite.java @@ -0,0 +1,136 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +public class Ignite extends Spell { + + public Ignite() { + super(Tier.BASIC, 5, Element.FIRE, "ignite", SpellType.ATTACK, 10, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + // Entity ray trace is done first because block ray trace passes through entities; if it was the other + // way round, entities would only be hit when there were no blocks in range behind them. + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + + EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; + + if(MagicDamage.isEntityImmune(DamageType.FIRE, target)){ + if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + }else{ + target.setFire((int)(10*modifiers.get(WizardryItems.duration_upgrade))); + } + + if(world.isRemote){ + double dx = target.posX - caster.posX; + double dy = (target.getEntityBoundingBox().minY + target.height/2) - WizardryUtilities.getPlayerEyesPos(caster); + double dz = target.posZ - caster.posZ; + // i starts at 1 so that particles are not spawned in the player's head. + for(int i=1;i<5;i++){ + //WizardryUtilities.spawnParticleAndNotify(world, EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 0, 0, 0, 0); + //WizardryUtilities.spawnParticleAndNotify(world, EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 0, 0, 0, 0); + world.spawnParticle(EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); + world.spawnParticle(EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); + } + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ITEM_FLINTANDSTEEL_USE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + + return true; + + }else{ + + rayTrace = WizardryUtilities.rayTrace(10*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + + // Gets block the player is looking at and sets the appropriate surrounding air block to fire. + // Note how the block is set on the server side only (kinda obvious really) but the particles are + // spawned on the client side only. + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + + BlockPos pos = rayTrace.getBlockPos().offset(rayTrace.sideHit); + + if(world.isAirBlock(pos)){ + if(!world.isRemote){ + world.setBlockState(pos, Blocks.FIRE.getDefaultState()); + } + + if(world.isRemote){ + + double dx = pos.getX() + 0.5 - caster.posX; + double dy = pos.getY() + 0.5 - WizardryUtilities.getPlayerEyesPos(caster); + double dz = pos.getZ() + 0.5 - caster.posZ; + + for(int i=1;i<5;i++){ + world.spawnParticle(EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); + world.spawnParticle(EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); + } + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ITEM_FLINTANDSTEEL_USE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + return true; + } + } + } + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!MagicDamage.isEntityImmune(DamageType.FIRE, target)) target.setFire((int)(10*modifiers.get(WizardryItems.duration_upgrade))); + + if(world.isRemote){ + double dx = target.posX - caster.posX; + double dy = (target.getEntityBoundingBox().minY + target.height/2) - (caster.posY + caster.getEyeHeight()); + double dz = target.posZ - caster.posZ; + // i starts at 1 so that particles are not spawned in the player's head. + for(int i=1;i<5;i++){ + //WizardryUtilities.spawnParticleAndNotify(world, EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 0, 0, 0, 0); + //WizardryUtilities.spawnParticleAndNotify(world, EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 0, 0, 0, 0); + world.spawnParticle(EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + caster.getEyeHeight() + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); + world.spawnParticle(EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + caster.getEyeHeight() + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); + } + } + + caster.swingArm(hand); + caster.playSound(SoundEvents.ITEM_FLINTANDSTEEL_USE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/ImbueWeapon.java b/src/main/java/electroblob/wizardry/spell/ImbueWeapon.java new file mode 100644 index 00000000..f2dbbcf6 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/ImbueWeapon.java @@ -0,0 +1,76 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryEnchantments; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.item.ItemBow; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemSword; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class ImbueWeapon extends Spell { + + public ImbueWeapon() { + super(Tier.APPRENTICE, 20, Element.SORCERY, "imbue_weapon", SpellType.UTILITY, 50, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + // Won't work if the weapon already has the enchantment + if(WizardData.get(caster) != null){ + + for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(caster)){ + + if(stack != null){ + + if(stack.getItem() instanceof ItemSword && !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.magic_sword) + && WizardData.get(caster).getImbuementDuration(WizardryEnchantments.magic_sword) <= 0){ + // The enchantment level as determined by the damage multiplier. The + 0.5f is so that + // weird float processing doesn't incorrectly round it down. + stack.addEnchantment(WizardryEnchantments.magic_sword, modifiers.get(SpellModifiers.DAMAGE) == 1.0f ? 1 : (int)((modifiers.get(SpellModifiers.DAMAGE) - 1.0f)/Constants.DAMAGE_INCREASE_PER_TIER + 0.5f)); + WizardData.get(caster).setImbuementDuration(WizardryEnchantments.magic_sword, (int)(900*modifiers.get(WizardryItems.duration_upgrade))); + + }else if(stack.getItem() instanceof ItemBow && !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.magic_bow) + && WizardData.get(caster).getImbuementDuration(WizardryEnchantments.magic_bow) <= 0){ + // The enchantment level as determined by the damage multiplier. The + 0.5f is so that + // weird float processing doesn't incorrectly round it down. + stack.addEnchantment(WizardryEnchantments.magic_bow, modifiers.get(SpellModifiers.DAMAGE) == 1.0f ? 1 : (int)((modifiers.get(SpellModifiers.DAMAGE) - 1.0f)/Constants.DAMAGE_INCREASE_PER_TIER + 0.5f)); + WizardData.get(caster).setImbuementDuration(WizardryEnchantments.magic_bow, (int)(900*modifiers.get(WizardryItems.duration_upgrade))); + + }else{ + continue; + } + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.9f, 0.7f, 1.0f); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); + return true; + } + } + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Intimidate.java b/src/main/java/electroblob/wizardry/spell/Intimidate.java new file mode 100644 index 00000000..20e3bbe9 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Intimidate.java @@ -0,0 +1,105 @@ +package electroblob.wizardry.spell; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityCreature; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.ai.RandomPositionGenerator; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.pathfinding.PathPoint; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class Intimidate extends Spell { + + /** The NBT tag name for storing the feared entity's UUID in the target's tag compound. Defined here in case + * it changes. */ + public static final String NBT_KEY = "fearedEntity"; + + public Intimidate() { + super(Tier.APPRENTICE, 20, Element.NECROMANCY, "intimidate", SpellType.ATTACK, 100, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + + List entities = WizardryUtilities.getEntitiesWithinRadius(8*modifiers.get(WizardryItems.range_upgrade), caster.posX, caster.posY, caster.posZ, world, EntityCreature.class); + + for(EntityCreature target : entities){ + + runAway(target, caster); + + NBTTagCompound entityNBT = target.getEntityData(); + if(entityNBT != null) entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID()); + + ((EntityLiving)target).addPotionEffect(new PotionEffect(WizardryPotions.fear, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0)); + + } + + }else{ + for(int i=0; i<30; i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, caster.posX - 1 + world.rand.nextDouble()*2, + caster.getEntityBoundingBox().minY + 1.5 + world.rand.nextDouble()*0.5, + caster.posZ - 1 + world.rand.nextDouble()*2, + 0, 0, 0, 0, 0.9f, 0.1f, 0.0f); + } + } + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERDRAGON_GROWL, 1.0f, 1.0f); + return true; + } + + /** + * Finds a random position away from the caster and sets the given target's AI path to that location. Defined here + * so it can be used both in the spell itself and in the potion effect (event handler). + * @param target The entity running away + * @param caster The entity that is being run away from + * @return True if a new path was found and set, false if not. + */ + public static boolean runAway(EntityCreature target, EntityLivingBase caster){ + + if(target.getDistanceToEntity(caster) < 16){ + + Vec3d Vec3d = RandomPositionGenerator.findRandomTargetBlockAwayFrom(target, 16, 7, new Vec3d(caster.posX, caster.posY, caster.posZ)); + + if (Vec3d == null){ + return false; + + }else{ + // In both cases it is necessary to check if the entity already has a path so it doesn't change direction + // every tick, unless that path is towards the caster. + //Path path = target.getNavigator().getPathToXYZ(Vec3d.xCoord, Vec3d.yCoord, Vec3d.zCoord); + + boolean flag = true; + + if(!target.getNavigator().noPath()){ + PathPoint point = target.getNavigator().getPath().getFinalPathPoint(); + if(point != null) flag = caster.getDistance(point.xCoord, point.yCoord, point.zCoord) < 16; + } + // Has a built in mind trick effect because for whatever reason this makes it work with skeletons. + target.setAttackTarget(null); + + if(flag) return target.getNavigator().tryMoveToXYZ(Vec3d.xCoord, Vec3d.yCoord, Vec3d.zCoord, 1.25);//target.getEntityAttribute(SharedMonsterAttributes.movementSpeed).getAttributeValue()); + } + } + + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java b/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java new file mode 100644 index 00000000..b4cbc77d --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java @@ -0,0 +1,54 @@ +package electroblob.wizardry.spell; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class InvigoratingPresence extends Spell { + + public InvigoratingPresence() { + super(Tier.APPRENTICE, 30, Element.HEALING, "invigorating_presence", SpellType.UTILITY, 60, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + List targets = WizardryUtilities.getEntitiesWithinRadius(5*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world, EntityPlayer.class); + + for(EntityPlayer target : targets){ + if(WizardryUtilities.isPlayerAlly(caster, target) || target == caster){ + // Strength 2 for 45 seconds. + target.addPotionEffect(new PotionEffect(MobEffects.STRENGTH, (int)(900*modifiers.get(WizardryItems.duration_upgrade)), 1, false, false)); + } + } + + if(world.isRemote){ + for(int i=0;i<50*modifiers.get(WizardryItems.blast_upgrade);i++){ + double radius = (1 + world.rand.nextDouble()*4)*modifiers.get(WizardryItems.blast_upgrade); + double angle = world.rand.nextDouble()*Math.PI*2; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + radius*Math.cos(angle), caster.getEntityBoundingBox().minY, caster.posZ + radius*Math.sin(angle), + 0, 0.03, 0, 50, 1, 0.2f, 0.2f); + + } + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Invisibility.java b/src/main/java/electroblob/wizardry/spell/Invisibility.java new file mode 100644 index 00000000..a26836d2 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Invisibility.java @@ -0,0 +1,72 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class Invisibility extends Spell { + + public Invisibility() { + super(Tier.ADVANCED, 35, Element.SORCERY, "invisibility", SpellType.UTILITY, 200, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + caster.addPotionEffect(new PotionEffect(MobEffects.INVISIBILITY, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0, false, false)); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.7f, 1.0f, 1.0f); + } + } + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(!caster.isPotionActive(MobEffects.INVISIBILITY)){ + + caster.addPotionEffect(new PotionEffect(MobEffects.INVISIBILITY, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0, false, false)); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)caster.posY + caster.getEyeHeight() - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.7f, 1.0f, 1.0f); + } + } + caster.playSound(WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/InvokeWeather.java b/src/main/java/electroblob/wizardry/spell/InvokeWeather.java new file mode 100644 index 00000000..98fd97c6 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/InvokeWeather.java @@ -0,0 +1,67 @@ +package electroblob.wizardry.spell; + +import java.util.Random; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +public class InvokeWeather extends Spell { + + public InvokeWeather() { + super(Tier.ADVANCED, 30, Element.LIGHTNING, "invoke_weather", SpellType.UTILITY, 100, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(caster.dimension == 0){ + + if(!world.isRemote){ + // TODO: Backport these changes. + int standardWeatherTime = (300 + (new Random()).nextInt(600)) * 20; + if(world.isRaining()){ + caster.addChatComponentMessage(new TextComponentTranslation("spell.invoke_weather.sun")); + world.getWorldInfo().setCleanWeatherTime(standardWeatherTime); + world.getWorldInfo().setRainTime(0); + world.getWorldInfo().setThunderTime(0); + world.getWorldInfo().setRaining(false); + world.getWorldInfo().setThundering(false); + }else{ + caster.addChatComponentMessage(new TextComponentTranslation("spell.invoke_weather.rain")); + world.getWorldInfo().setCleanWeatherTime(0); + world.getWorldInfo().setRainTime(standardWeatherTime); + world.getWorldInfo().setThunderTime(standardWeatherTime); + world.getWorldInfo().setRaining(true); + // 1/3 chance for a thunderstorm + world.getWorldInfo().setThundering(world.rand.nextInt(3) == 0); + } + } + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.5f, 0.7f, 1.0f); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_LIGHTNING_THUNDER, 0.5F, 1.0f); + return true; + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Ironflesh.java b/src/main/java/electroblob/wizardry/spell/Ironflesh.java new file mode 100644 index 00000000..0997439b --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Ironflesh.java @@ -0,0 +1,73 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class Ironflesh extends Spell { + + public Ironflesh() { + super(Tier.ADVANCED, 30, Element.HEALING, "ironflesh", SpellType.DEFENCE, 100, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + caster.addPotionEffect(new PotionEffect(MobEffects.RESISTANCE, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 2, false, false)); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.4f, 0.5f, 0.6f); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(!caster.isPotionActive(MobEffects.RESISTANCE)){ + + caster.addPotionEffect(new PotionEffect(MobEffects.RESISTANCE, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 2, false, false)); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)caster.posY + caster.getEyeHeight() - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.4f, 0.5f, 0.6f); + } + } + + caster.playSound(WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Leap.java b/src/main/java/electroblob/wizardry/spell/Leap.java new file mode 100644 index 00000000..49c2265c --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Leap.java @@ -0,0 +1,47 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.world.World; + +public class Leap extends Spell { + + public Leap() { + super(Tier.BASIC, 10, Element.EARTH, "leap", SpellType.UTILITY, 20, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(caster.onGround){ + + caster.motionY = 0.65 * modifiers.get(SpellModifiers.DAMAGE); + caster.addVelocity(caster.getLookVec().xCoord*0.3, 0, caster.getLookVec().zCoord*0.3); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x = (double)(caster.posX + world.rand.nextFloat() - 0.5F); + double y = (double)(caster.getEntityBoundingBox().minY); + double z = (double)(caster.posZ + world.rand.nextFloat() - 0.5F); + world.spawnParticle(EnumParticleTypes.CLOUD, x, y, z, 0, 0, 0); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERDRAGON_FLAP, 0.5F, 1.0f); + caster.swingArm(hand); + return true; + } + + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Levitation.java b/src/main/java/electroblob/wizardry/spell/Levitation.java new file mode 100644 index 00000000..4cff7b9e --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Levitation.java @@ -0,0 +1,39 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class Levitation extends Spell { + + public Levitation() { + super(Tier.ADVANCED, 10, Element.SORCERY, "levitation", SpellType.UTILITY, 0, EnumAction.BOW, true); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + caster.fallDistance = 0; + + caster.motionY = caster.motionY < 0.5d ? caster.motionY + 0.1d : caster.motionY; + + if(world.isRemote){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX - 0.25d + world.rand.nextDouble()/2, WizardryUtilities.getPlayerEyesPos(caster) - 1.5f, caster.posZ - 0.25d + world.rand.nextDouble()/2, 0, -0.1F, 0, 15, 0.5f, 1.0f, 0.7f); + } + if(ticksInUse % 24 == 0 && world.isRemote){ + Wizardry.proxy.playMovingSound(caster, WizardrySounds.SPELL_LOOP_SPARKLE, 0.5F, 1.0f, false); + } + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/LifeDrain.java b/src/main/java/electroblob/wizardry/spell/LifeDrain.java new file mode 100644 index 00000000..cd2368b6 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/LifeDrain.java @@ -0,0 +1,102 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class LifeDrain extends Spell { + + public LifeDrain() { + super(Tier.APPRENTICE, 10, Element.NECROMANCY, "life_drain", SpellType.ATTACK, 0, EnumAction.NONE, true); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + Vec3d look = caster.getLookVec(); + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + + EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; + + if(ticksInUse % 12 == 0){ + WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeDirectMagicDamage(caster, DamageType.MAGIC), 2.0f * modifiers.get(SpellModifiers.DAMAGE)); + caster.heal(1); + } + } + if(world.isRemote){ + for(int i=5; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + // I figured it out! when on client side, entityplayer.posY is at the eyes, not the feet! + double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + //world.spawnParticle("mobSpell", x1, y1, z1, -1*look.xCoord, -1*look.yCoord, -1*look.zCoord); + if(i % 5 == 0){ + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + } + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, -0.05*look.xCoord*i, -0.05*look.yCoord*i, -0.05*look.zCoord*i, 8 + world.rand.nextInt(6), 0.5f, 0.0f, 0.0f); + } + } + if(ticksInUse % 18 == 0){ + if(ticksInUse == 0) WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 1.0F, 0.6f); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_CRACKLE, 2.0F, 1.0f); + } + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + + Vec3d vec = new Vec3d(target.posX - caster.posX, target.posY - caster.posY, target.posZ - caster.posZ).normalize(); + + if(target != null){ + if(ticksInUse % 12 == 0){ + WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeDirectMagicDamage(caster, DamageType.MAGIC), 2.0f * modifiers.get(SpellModifiers.DAMAGE)); + caster.heal(1); + } + } + if(world.isRemote){ + for(int i=5; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + // I figured it out! when on client side, entityplayer.posY is at the eyes, not the feet! + double x1 = caster.posX + vec.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = caster.posY + caster.getEyeHeight() - 0.4f + vec.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + vec.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + //world.spawnParticle("mobSpell", x1, y1, z1, -1*look.xCoord, -1*look.yCoord, -1*look.zCoord); + if(i % 5 == 0){ + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + } + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, -0.05*vec.xCoord*i, -0.05*vec.yCoord*i, -0.05*vec.zCoord*i, 8 + world.rand.nextInt(6), 0.5f, 0.0f, 0.0f); + } + } + if(ticksInUse % 18 == 0){ + if(ticksInUse == 0) caster.playSound(WizardrySounds.SPELL_SUMMONING, 1.0F, 0.6f); + caster.playSound(WizardrySounds.SPELL_LOOP_CRACKLE, 2.0F, 1.0f); + } + + return true; + } + + @Override + public boolean canBeCastByNPCs() { + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Light.java b/src/main/java/electroblob/wizardry/spell/Light.java new file mode 100644 index 00000000..96d056c8 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Light.java @@ -0,0 +1,77 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryBlocks; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.tileentity.TileEntityTimer; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class Light extends Spell { + + public Light() { + super(Tier.BASIC, 5, Element.SORCERY, "light", SpellType.UTILITY, 15, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.rayTrace(4, world, caster, false); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + + BlockPos pos = rayTrace.getBlockPos().offset(rayTrace.sideHit); + + if(world.isAirBlock(pos)){ + + if(!world.isRemote){ + world.setBlockState(pos, WizardryBlocks.magic_light.getDefaultState()); + if(world.getTileEntity(pos) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(pos)).setLifetime((int)(600*modifiers.get(WizardryItems.duration_upgrade))); + } + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); + return true; + } + }else{ + + int x = (int) (Math.floor(caster.posX) + caster.getLookVec().xCoord*4); + int y = (int) (Math.floor(caster.posY) + caster.eyeHeight + caster.getLookVec().yCoord*4); + int z = (int) (Math.floor(caster.posZ) + caster.getLookVec().zCoord*4); + + BlockPos pos = new BlockPos(x, y, z); + + if(world.isAirBlock(pos)){ + //world.playSound(x, y, z, "sound.ambient.cave.cave", 1.0f, 1.5f, false); + if(!world.isRemote){ + world.setBlockState(pos, WizardryBlocks.magic_light.getDefaultState()); + if(world.getTileEntity(pos) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(pos)).setLifetime((int)(600*modifiers.get(WizardryItems.duration_upgrade))); + } + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); + return true; + } + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/LightningArrow.java b/src/main/java/electroblob/wizardry/spell/LightningArrow.java new file mode 100644 index 00000000..423fb35e --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/LightningArrow.java @@ -0,0 +1,65 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.projectile.EntityLightningArrow; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class LightningArrow extends Spell { + + public LightningArrow() { + super(Tier.APPRENTICE, 15, Element.LIGHTNING, "lightning_arrow", SpellType.ATTACK, 20, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + EntityLightningArrow lightningArrow = new EntityLightningArrow(world, caster, 2*modifiers.get(WizardryItems.range_upgrade), modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(lightningArrow); + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1.0F, world.rand.nextFloat() * 0.3F + 1.3F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + EntityLightningArrow lightningArrow = new EntityLightningArrow(world, caster, target, 2*modifiers.get(WizardryItems.range_upgrade), 4, modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(lightningArrow); + } + + caster.swingArm(hand); + caster.playSound(WizardrySounds.SPELL_LIGHTNING, 1.0F, world.rand.nextFloat() * 0.3F + 1.3F); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/LightningBolt.java b/src/main/java/electroblob/wizardry/spell/LightningBolt.java new file mode 100644 index 00000000..9adbc736 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/LightningBolt.java @@ -0,0 +1,90 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.effect.EntityLightningBolt; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class LightningBolt extends Spell { + + public LightningBolt() { + super(Tier.ADVANCED, 40, Element.LIGHTNING, "lightning_bolt", SpellType.ATTACK, 80, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.rayTrace(200, world, caster, false); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + + BlockPos pos = rayTrace.getBlockPos(); + + // Not sure why it is up 1 but it has to be for canBlockSeeSky to work properly. + // TODO: Remove this requirement? + if(world.canBlockSeeSky(pos.up())){ + + if(!world.isRemote){ + EntityLightningBolt entitylightning = new EntityLightningBolt(world, pos.getX(), pos.getY(), pos.getZ(), false); + world.addWeatherEffect(entitylightning); + + // Code for eventhandler recognition; for achievements and such like. Left in for future use. + NBTTagCompound entityNBT = entitylightning.getEntityData(); + entityNBT.setUniqueId("summoningPlayer", caster.getUniqueID()); + } + + caster.swingArm(hand); + return true; + } + } + + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + int x = (int)target.posX; + int y = (int)target.posY; + int z = (int)target.posZ; + + // Not sure why it is up 1 but it has to be for canBlockSeeSky to work properly. + // TODO: Remove this requirement? + if(world.canBlockSeeSky(new BlockPos(x, y, z))){ + + if(!world.isRemote){ + EntityLightningBolt entitylightning = new EntityLightningBolt(world, x, y, z, false); + world.addWeatherEffect(entitylightning); + } + + caster.swingArm(hand); + return true; + } + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } +} diff --git a/src/main/java/electroblob/wizardry/spell/LightningDisc.java b/src/main/java/electroblob/wizardry/spell/LightningDisc.java new file mode 100644 index 00000000..fe0d5f18 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/LightningDisc.java @@ -0,0 +1,65 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.projectile.EntityLightningDisc; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class LightningDisc extends Spell { + + public LightningDisc() { + super(Tier.ADVANCED, 25, Element.LIGHTNING, "lightning_disc", SpellType.ATTACK, 60, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + EntityLightningDisc lightningdisc = new EntityLightningDisc(world, caster, modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(lightningdisc); + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1.0F, world.rand.nextFloat() * 0.3F + 0.8F); + caster.swingArm(hand); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + EntityLightningDisc lightningdisc = new EntityLightningDisc(world, caster, modifiers.get(SpellModifiers.DAMAGE)); + lightningdisc.directTowards(target, 1.2f); + world.spawnEntityInWorld(lightningdisc); + } + + caster.playSound(WizardrySounds.SPELL_LIGHTNING, 1.0F, world.rand.nextFloat() * 0.3F + 0.8F); + caster.swingArm(hand); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/LightningHammer.java b/src/main/java/electroblob/wizardry/spell/LightningHammer.java new file mode 100644 index 00000000..a7408c2e --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/LightningHammer.java @@ -0,0 +1,61 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.construct.EntityHammer; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class LightningHammer extends Spell { + + public LightningHammer() { + super(Tier.MASTER, 100, Element.LIGHTNING, "lightning_hammer", SpellType.ATTACK, 300, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.rayTrace(40*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + + BlockPos pos = rayTrace.getBlockPos(); + + // Not sure why it is +1 but it has to be to work properly. + if(world.canBlockSeeSky(pos.up())){ + + if(!world.isRemote){ + + EntityHammer hammer = new EntityHammer(world, pos.getX()+0.5, pos.getY()+50, pos.getZ()+0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + + hammer.motionX = 0; + hammer.motionY = -2; + hammer.motionZ = 0; + + world.spawnEntityInWorld(hammer); + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 3.0f, 1.0f); + return true; + } + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/LightningPulse.java b/src/main/java/electroblob/wizardry/spell/LightningPulse.java new file mode 100644 index 00000000..f180b10d --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/LightningPulse.java @@ -0,0 +1,80 @@ +package electroblob.wizardry.spell; + +import java.util.List; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.construct.EntityLightningPulse; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.EnumAction; +import net.minecraft.network.play.server.SPacketEntityVelocity; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.World; + +public class LightningPulse extends Spell { + + public LightningPulse() { + super(Tier.ADVANCED, 25, Element.LIGHTNING, "lightning_pulse", SpellType.ATTACK, 75, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(caster.onGround){ + + List targets = WizardryUtilities.getEntitiesWithinRadius(3.0d*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world); + + for(EntityLivingBase target : targets){ + if(WizardryUtilities.isValidTarget(caster, target)){ + // Damage is 4 hearts no matter where the target is. + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 8 * modifiers.get(SpellModifiers.DAMAGE)); + + if(!world.isRemote){ + + double dx = target.posX - caster.posX; + double dz = target.posZ - caster.posZ; + // Normalises the velocity. + double vectorLength = MathHelper.sqrt_double(dx*dx + dz*dz); + dx /= vectorLength; + dz /= vectorLength; + + target.motionX = 0.8 * dx; + target.motionY = 0; + target.motionZ = 0.8 * dz; + + // Player motion is handled on that player's client so needs packets + if(target instanceof EntityPlayerMP){ + ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + } + } + } + } + if(!world.isRemote){ + EntityLightningPulse lightningpulse = new EntityLightningPulse(world, caster.posX, caster.getEntityBoundingBox().minY, caster.posZ, caster, 7, modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(lightningpulse); + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1.0f, 1.0f); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SHOCKWAVE, 2.0f, 1.0f); + return true; + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/LightningRay.java b/src/main/java/electroblob/wizardry/spell/LightningRay.java new file mode 100644 index 00000000..b956bd49 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/LightningRay.java @@ -0,0 +1,143 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.EntityArc; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +public class LightningRay extends Spell { + + public LightningRay() { + super(Tier.APPRENTICE, 5, Element.LIGHTNING, "lightning_ray", SpellType.ATTACK, 0, EnumAction.NONE, true); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade), 2.0f); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + Entity target = rayTrace.entityHit; + if(!world.isRemote){ + // This statement means the arc only spawns every other tick. + if(ticksInUse % 2 == 0){ + + EntityArc arc = new EntityArc(world); + // The look vec stuff performs a translation on the start point to line it up with the wand. + // EDIT: removed due to 1st/3rd person render differences. + arc.setEndpointCoords(caster.posX, caster.posY + 1.2, caster.posZ, + target.posX, target.posY + target.height/2, target.posZ); + + arc.lifetime = 1; + + world.spawnEntityInWorld(arc); + } + + if(MagicDamage.isEntityImmune(DamageType.SHOCK, target)){ + if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + }else{ + WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 3.0f * modifiers.get(SpellModifiers.DAMAGE)); + } + + }else{ + for(int i=0;i<5;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + } + } + + if(ticksInUse == 1){ + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1.0F, 1.0f); + }else if(ticksInUse > 0 && ticksInUse % 20 == 0){ + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_LIGHTNING, 1.0F, 1.0f); + } + + return true; + + }else{ + if(!world.isRemote){ + // This statement means the arc only spawns every other tick. + if(ticksInUse % 2 == 0){ + + EntityArc arc = new EntityArc(world); + + arc.setEndpointCoords(caster.posX, caster.posY + 1.2, caster.posZ, + caster.posX + caster.getLookVec().xCoord * 8, caster.posY + caster.eyeHeight + caster.getLookVec().yCoord * 8, caster.posZ + caster.getLookVec().zCoord * 8); + + arc.lifetime = 1; + + world.spawnEntityInWorld(arc); + } + } + + if(ticksInUse == 1){ + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1.0F, 1.0f); + }else if(ticksInUse > 0 && ticksInUse % 20 == 0){ + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_LIGHTNING, 1.0F, 1.0f); + } + + return true; + } + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + if(!world.isRemote){ + // This statement means the arc only spawns every other tick. + if(ticksInUse % 2 == 0){ + + EntityArc arc = new EntityArc(world); + // The look vec stuff performs a translation on the start point to line it up with the wand. + // EDIT: removed due to 1st/3rd person render differences. + arc.setEndpointCoords(caster.posX, caster.posY + 1.2, caster.posZ, + target.posX, target.posY + target.height/2, target.posZ); + + arc.lifetime = 1; + + world.spawnEntityInWorld(arc); + } + + WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 3.0f * modifiers.get(SpellModifiers.DAMAGE)); + + }else{ + for(int i=0;i<5;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + } + } + + if(ticksInUse == 1){ + caster.playSound(WizardrySounds.SPELL_LIGHTNING, 1.0F, 1.0f); + }else if(ticksInUse > 0 && ticksInUse % 20 == 0){ + caster.playSound(WizardrySounds.SPELL_LOOP_LIGHTNING, 1.0F, 1.0f); + } + + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/LightningSigil.java b/src/main/java/electroblob/wizardry/spell/LightningSigil.java new file mode 100644 index 00000000..831a1189 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/LightningSigil.java @@ -0,0 +1,52 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.construct.EntityLightningSigil; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class LightningSigil extends Spell { + + public LightningSigil() { + super(Tier.APPRENTICE, 10, Element.LIGHTNING, "lightning_sigil", SpellType.ATTACK, 20, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.rayTrace(10*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK && rayTrace.sideHit == EnumFacing.UP){ + + if(!world.isRemote){ + double x = rayTrace.hitVec.xCoord; + double y = rayTrace.hitVec.yCoord; + double z = rayTrace.hitVec.zCoord; + EntityLightningSigil lightningsigil = new EntityLightningSigil(world, x, y, z, caster, modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(lightningsigil); + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0F, 0.3F); + return true; + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/LightningWeb.java b/src/main/java/electroblob/wizardry/spell/LightningWeb.java new file mode 100644 index 00000000..3b654be1 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/LightningWeb.java @@ -0,0 +1,193 @@ +package electroblob.wizardry.spell; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.EntityArc; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +public class LightningWeb extends Spell { + + public LightningWeb() { + super(Tier.MASTER, 15, Element.LIGHTNING, "lightning_web", SpellType.ATTACK, 0, EnumAction.NONE, true); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade), 2.0f); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + + Entity target = rayTrace.entityHit; + + if(!world.isRemote){ + + // This statement means the arc only spawns every other tick. + if(ticksInUse % 2 == 0){ + EntityArc arc = new EntityArc(world); + // The look vec stuff performs a translation on the start point to line it up with the wand. + // EDIT: removed due to 1st/3rd person render differences. + arc.setEndpointCoords(caster.posX, caster.posY + 1.2, caster.posZ, + target.posX, target.posY + target.height/2, target.posZ); + arc.lifetime = 1; + world.spawnEntityInWorld(arc); + } + + if(MagicDamage.isEntityImmune(DamageType.SHOCK, target)){ + if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + }else{ + // This motion stuff removes knockback, which is desirable for continuous spells. + double motionX = target.motionX; + double motionY = target.motionY; + double motionZ = target.motionZ; + + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 5.0f * modifiers.get(SpellModifiers.DAMAGE)); + + target.motionX = motionX; + target.motionY = motionY; + target.motionZ = motionZ; + } + }else{ + for(int i=0;i<5;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + } + } + + // Secondary chaining effect + double seekerRange = 5.0d; + + List secondaryTargets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, target.posX, target.posY + target.height/2, target.posZ, world); + // This is a MUCH better way of filtering the secondary targets! + secondaryTargets.remove(target); + if(secondaryTargets.size() > 5) secondaryTargets = secondaryTargets.subList(0, 5); + + for(EntityLivingBase secondaryTarget : secondaryTargets){ + + if(WizardryUtilities.isValidTarget(caster, secondaryTarget)){ + + if(!world.isRemote){ + // This statement means the arc only spawns every other tick. + if(ticksInUse % 2 == 0){ + EntityArc arc = new EntityArc(world); + arc.setEndpointCoords(target.posX, target.posY + 1.2, target.posZ, + secondaryTarget.posX, secondaryTarget.posY + secondaryTarget.height/2, secondaryTarget.posZ); + arc.lifetime = 1; + world.spawnEntityInWorld(arc); + } + + if(MagicDamage.isEntityImmune(DamageType.SHOCK, secondaryTarget)){ + if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", secondaryTarget.getName(), this.getNameForTranslationFormatted())); + }else{ + // This motion stuff removes knockback, which is desirable for continuous spells. + double motionX = secondaryTarget.motionX; + double motionY = secondaryTarget.motionY; + double motionZ = secondaryTarget.motionZ; + + secondaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 4.0f * modifiers.get(SpellModifiers.DAMAGE)); + + secondaryTarget.motionX = motionX; + secondaryTarget.motionY = motionY; + secondaryTarget.motionZ = motionZ; + } + }else{ + for(int i=0;i<5;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, secondaryTarget.posX + world.rand.nextFloat() - 0.5, secondaryTarget.getEntityBoundingBox().minY + secondaryTarget.height/2 + world.rand.nextFloat()*2 - 1, secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + } + } + + // Tertiary chaining effect + + List tertiaryTargets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, secondaryTarget.posX, secondaryTarget.posY + secondaryTarget.height/2, secondaryTarget.posZ, world); + tertiaryTargets.remove(target); + tertiaryTargets.removeAll(secondaryTargets); + if(tertiaryTargets.size() > 2) tertiaryTargets = tertiaryTargets.subList(0, 2); + + for(EntityLivingBase tertiaryTarget : tertiaryTargets){ + + if(WizardryUtilities.isValidTarget(caster, tertiaryTarget)){ + + if(!world.isRemote){ + // This statement means the arc only spawns every other tick. + if(ticksInUse % 2 == 0){ + EntityArc arc = new EntityArc(world); + arc.setEndpointCoords(secondaryTarget.posX, secondaryTarget.posY + 1.2, secondaryTarget.posZ, + tertiaryTarget.posX, tertiaryTarget.posY + tertiaryTarget.height/2, tertiaryTarget.posZ); + arc.lifetime = 1; + world.spawnEntityInWorld(arc); + } + + if(MagicDamage.isEntityImmune(DamageType.SHOCK, tertiaryTarget)){ + if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", tertiaryTarget.getName(), this.getNameForTranslationFormatted())); + }else{ + // This motion stuff removes knockback, which is desirable for continuous spells. + double motionX = tertiaryTarget.motionX; + double motionY = tertiaryTarget.motionY; + double motionZ = tertiaryTarget.motionZ; + + tertiaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 3.0f * modifiers.get(SpellModifiers.DAMAGE)); + + tertiaryTarget.motionX = motionX; + tertiaryTarget.motionY = motionY; + tertiaryTarget.motionZ = motionZ; + } + }else{ + for(int i=0;i<5;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, tertiaryTarget.posX + world.rand.nextFloat() - 0.5, tertiaryTarget.getEntityBoundingBox().minY + tertiaryTarget.height/2 + world.rand.nextFloat()*2 - 1, tertiaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + } + } + } + } + } + } + + if(ticksInUse == 1){ + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1.0F, 1.0f); + }else if(ticksInUse > 0 && ticksInUse % 20 == 0){ + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_LIGHTNING, 1.0F, 1.0f); + } + + return true; + + }else{ + if(!world.isRemote){ + // This statement means the arc only spawns every other tick. + if(ticksInUse % 2 == 0){ + EntityArc arc = new EntityArc(world); + arc.setEndpointCoords(caster.posX, caster.posY + 1.2, caster.posZ, + caster.posX + caster.getLookVec().xCoord * 8, caster.posY + caster.eyeHeight + caster.getLookVec().yCoord * 8, caster.posZ + caster.getLookVec().zCoord * 8); + arc.lifetime = 1; + //arc.setOffset(entityplayer.getLookVec().zCoord * 0.5, entityplayer.getLookVec().xCoord * 0.5); + world.spawnEntityInWorld(arc); + } + } + + if(ticksInUse == 1){ + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1.0F, 1.0f); + }else if(ticksInUse > 0 && ticksInUse % 20 == 0){ + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_LIGHTNING, 1.0F, 1.0f); + } + + return true; + } + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/MagicMissile.java b/src/main/java/electroblob/wizardry/spell/MagicMissile.java new file mode 100644 index 00000000..9cb083e3 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/MagicMissile.java @@ -0,0 +1,67 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.projectile.EntityMagicMissile; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class MagicMissile extends Spell { + + public MagicMissile() { + super(Tier.BASIC, 5, Element.MAGIC, "magic_missile", SpellType.ATTACK, 10, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + EntityMagicMissile magicMissile = new EntityMagicMissile(world, caster, 2*modifiers.get(WizardryItems.range_upgrade), modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(magicMissile); + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_MAGIC, 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); + + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + EntityMagicMissile magicMissile = new EntityMagicMissile(world, caster, target, 2*modifiers.get(WizardryItems.range_upgrade), 4, modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(magicMissile); + } + + caster.swingArm(hand); + caster.playSound(WizardrySounds.SPELL_MAGIC, 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); + + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Metamorphosis.java b/src/main/java/electroblob/wizardry/spell/Metamorphosis.java new file mode 100644 index 00000000..abfceedc --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Metamorphosis.java @@ -0,0 +1,132 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityCaveSpider; +import net.minecraft.entity.monster.EntityMagmaCube; +import net.minecraft.entity.monster.EntityPigZombie; +import net.minecraft.entity.monster.EntitySkeleton; +import net.minecraft.entity.monster.EntitySlime; +import net.minecraft.entity.monster.EntitySpider; +import net.minecraft.entity.monster.SkeletonType; +import net.minecraft.entity.passive.EntityBat; +import net.minecraft.entity.passive.EntityChicken; +import net.minecraft.entity.passive.EntityCow; +import net.minecraft.entity.passive.EntityMooshroom; +import net.minecraft.entity.passive.EntityPig; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class Metamorphosis extends Spell { + + public Metamorphosis() { + super(Tier.APPRENTICE, 15, Element.NECROMANCY, "metamorphosis", SpellType.UTILITY, 30, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + Vec3d look = caster.getLookVec(); + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){ + + Entity entityHit = rayTrace.entityHit; + double xPos = entityHit.posX; + double yPos = entityHit.posY; + double zPos = entityHit.posZ; + + EntityLiving newEntity = null; + boolean flag = false; + + if(entityHit instanceof EntityPig){ + newEntity = new EntityPigZombie(world); + } + else if(entityHit instanceof EntityPigZombie){ + newEntity = new EntityPig(world); + } + else if(entityHit instanceof EntitySkeleton){ + // IDEA: Interaction with husks/strays? + if(((EntitySkeleton)entityHit).getSkeletonType() == SkeletonType.NORMAL){ + ((EntitySkeleton)entityHit).setSkeletonType(SkeletonType.WITHER); + }else{ + ((EntitySkeleton)entityHit).setSkeletonType(SkeletonType.NORMAL); + } + flag = true; + } + else if(entityHit instanceof EntityCow && !(entityHit instanceof EntityMooshroom)){ + newEntity = new EntityMooshroom(world); + } + else if(entityHit instanceof EntityMooshroom){ + newEntity = new EntityCow(world); + } + else if(entityHit instanceof EntityChicken){ + newEntity = new EntityBat(world); + } + else if(entityHit instanceof EntityBat){ + newEntity = new EntityChicken(world); + } + else if(entityHit instanceof EntitySlime && !(entityHit instanceof EntityMagmaCube)){ + newEntity = new EntityMagmaCube(world); + } + else if(entityHit instanceof EntityMagmaCube){ + newEntity = new EntitySlime(world); + } + else if(entityHit instanceof EntitySpider && !(entityHit instanceof EntityCaveSpider)){ + newEntity = new EntityCaveSpider(world); + } + else if(entityHit instanceof EntityCaveSpider){ + newEntity = new EntitySpider(world); + } + + if(newEntity != null || flag){ + + if(!world.isRemote && newEntity != null){ + //Transfers attributes from the old entity to the new one. + newEntity.setHealth(((EntityLiving)entityHit).getHealth()); + //newEntity.writeToNBT(entityHit.getEntityData()); + + entityHit.setDead(); + newEntity.setPosition(xPos, yPos, zPos); + world.spawnEntityInWorld(newEntity); + } + + if(world.isRemote){ + for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + // I figured it out! when on client side, entityplayer.posY is at the eyes, not the feet! + double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + //world.spawnParticle("mobSpell", x1, y1, z1, -1*look.xCoord, -1*look.yCoord, -1*look.zCoord); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.2f, 0.0f, 0.1f); + } + for(int i=0;i<5;i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, xPos, yPos, zPos, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + } + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_DEFLECTION, 0.5F, 0.8f); + return true; + } + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Meteor.java b/src/main/java/electroblob/wizardry/spell/Meteor.java new file mode 100644 index 00000000..af6c4aa3 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Meteor.java @@ -0,0 +1,55 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.EntityMeteor; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class Meteor extends Spell { + + public Meteor() { + super(Tier.MASTER, 100, Element.FIRE, "meteor", SpellType.ATTACK, 200, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.rayTrace(40*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + + BlockPos pos = rayTrace.getBlockPos(); + + // Not sure why it is +1 but it has to be to work properly. + if(world.canBlockSeeSky(pos.up())){ + + if(!world.isRemote){ + EntityMeteor meteor = new EntityMeteor(world, pos.getX(), pos.getY() + 50, pos.getZ(), modifiers.get(WizardryItems.blast_upgrade)); + world.spawnEntityInWorld(meteor); + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 3.0f, 1.0f); + return true; + } + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/MindControl.java b/src/main/java/electroblob/wizardry/spell/MindControl.java new file mode 100644 index 00000000..0cb6c6a2 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/MindControl.java @@ -0,0 +1,169 @@ +package electroblob.wizardry.spell; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntityWizard; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.INpc; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +public class MindControl extends Spell { + + /** The NBT tag name for storing the controlling entity's UUID in the target's tag compound. Defined here in case + * it changes. */ + public static final String NBT_KEY = "controllingEntity"; + + public MindControl() { + super(Tier.ADVANCED, 40, Element.NECROMANCY, "mind_control", SpellType.ATTACK, 150, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){ + + Entity target = rayTrace.entityHit; + + if(!world.isRemote){ + if(target instanceof EntityPlayer || !target.isNonBoss() || target instanceof INpc){ + // Adds a message saying that the player/boss entity/wizard resisted mind control + if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + + }else if(target instanceof EntityLiving){ + + if(!MindControl.findMindControlTarget((EntityLiving)target, caster, world)){ + // If no valid target was found, this just acts like mind trick. + ((EntityLiving)target).setAttackTarget(null); + } + + NBTTagCompound entityNBT = target.getEntityData(); + if(entityNBT != null) entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID()); + + ((EntityLiving)target).addPotionEffect(new PotionEffect(WizardryPotions.mind_control, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0)); + } + }else{ + for(int i=0; i<10; i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, + target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, + target.posZ - 0.25 + world.rand.nextDouble()*0.5, + 0, 0, 0, 0, 0.8f, 0.2f, 1.0f); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, + target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, + target.posZ - 0.25 + world.rand.nextDouble()*0.5, + 0, 0, 0, 0, 0.2f, 0.04f, 0.25f); + } + } + target.playSound(WizardrySounds.SPELL_SUMMONING, 1.0f, 1.0f); + caster.swingArm(hand); + return true; + } + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + + if(target != null){ + if(!world.isRemote){ + if(target instanceof EntityLiving && target.isNonBoss() + && !(target instanceof EntityWizard)){ + + if(!MindControl.findMindControlTarget((EntityLiving)target, caster, world)){ + // If no valid target was found, this just acts like mind trick. + ((EntityLiving)target).setAttackTarget(null); + } + + NBTTagCompound entityNBT = target.getEntityData(); + if(entityNBT != null) entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID()); + + ((EntityLiving)target).addPotionEffect(new PotionEffect(WizardryPotions.mind_control, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0)); + } + }else{ + for(int i=0; i<10; i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, + target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, + target.posZ - 0.25 + world.rand.nextDouble()*0.5, + 0, 0, 0, 0, 0.8f, 0.2f, 1.0f); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, + target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, + target.posZ - 0.25 + world.rand.nextDouble()*0.5, + 0, 0, 0, 0, 0.2f, 0.04f, 0.25f); + } + } + target.playSound(WizardrySounds.SPELL_SUMMONING, 1.0f, 1.0f); + caster.swingArm(hand); + return true; + } + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + + /** + * Finds the nearest creature to the given target which it is allowed to attack according to the given caster + * and sets it as the target's attack target. Handles both new and old AI and takes follow range into account. + * Defined here so it can be used both in the spell itself and in the potion effect (event handler). + * @param target The entity being mind controlled + * @param caster The entity doing the controlling + * @param world The world to look for targets in + * @return True if a new target was found and set, false if not. + */ + public static boolean findMindControlTarget(EntityLiving target, EntityLivingBase caster, World world){ + + // As of 1.1, this now uses the creature's follow range, like normal targeting. It also + // no longer lasts until the creature dies; instead it is a potion effect which continues to + // set the target until it wears off. + List possibleTargets = WizardryUtilities.getEntitiesWithinRadius( + ((EntityLiving)target).getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).getAttributeValue(), + target.posX, target.posY, target.posZ, world); + + possibleTargets.remove(target); + + EntityLivingBase newAITarget = null; + + for(EntityLivingBase possibleTarget : possibleTargets){ + if(WizardryUtilities.isValidTarget(caster, possibleTarget) && (newAITarget == null + || target.getDistanceToEntity(possibleTarget) < target.getDistanceToEntity(newAITarget))){ + newAITarget = possibleTarget; + } + } + + if(newAITarget != null){ + // From 1.7.10 - this seems not to work quite right; the entity appears to continue attacking this target + // after it gets killed (not noticeable in survival since it will target the player again immediately.) + ((EntityLiving)target).setAttackTarget(newAITarget); + + return true; + } + + return false; + + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/MindTrick.java b/src/main/java/electroblob/wizardry/spell/MindTrick.java new file mode 100644 index 00000000..2771e47a --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/MindTrick.java @@ -0,0 +1,100 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class MindTrick extends Spell { + + public MindTrick() { + super(Tier.BASIC, 10, Element.NECROMANCY, "mind_trick", SpellType.ATTACK, 40, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){ + + EntityLivingBase target = (EntityLivingBase)rayTrace.entityHit; + + if(!world.isRemote){ + + if(target instanceof EntityPlayer){ + + target.addPotionEffect(new PotionEffect(MobEffects.NAUSEA, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0)); + + }else if(target instanceof EntityLiving){ + + ((EntityLiving)target).setAttackTarget(null); + target.addPotionEffect(new PotionEffect(WizardryPotions.mind_trick, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0)); + } + }else{ + for(int i=0; i<10; i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, + target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, + target.posZ - 0.25 + world.rand.nextDouble()*0.5, + 0, 0, 0, 0, 0.8f, 0.2f, 1.0f); + } + } + + target.playSound(WizardrySounds.SPELL_DEFLECTION, 0.7F, world.rand.nextFloat() * 0.4F + 0.8F); + caster.swingArm(hand); + return true; + } + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + + if(target != null){ + if(!world.isRemote){ + if(target instanceof EntityPlayer){ + + target.addPotionEffect(new PotionEffect(MobEffects.NAUSEA, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0)); + + }else if(target instanceof EntityLiving){ + + ((EntityLiving)target).setAttackTarget(null); + target.addPotionEffect(new PotionEffect(WizardryPotions.mind_trick, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0)); + + } + }else{ + for(int i=0; i<10; i++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, + target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, + target.posZ - 0.25 + world.rand.nextDouble()*0.5, + 0, 0, 0, 0, 0.8f, 0.2f, 1.0f); + } + } + + target.playSound(WizardrySounds.SPELL_DEFLECTION, 0.7F, world.rand.nextFloat() * 0.4F + 0.8F); + caster.swingArm(hand); + return true; + } + return false; + } + + @Override + public boolean canBeCastByNPCs() { + return true; + } +} diff --git a/src/main/java/electroblob/wizardry/spell/None.java b/src/main/java/electroblob/wizardry/spell/None.java new file mode 100644 index 00000000..12566e04 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/None.java @@ -0,0 +1,32 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +/** This class represents a blank spell used to fill empty slots on wands. It is unobtainable in-game, except via + * commands, and does nothing when the player attempts to cast it. Its instance can be referenced directly using + * {@link electroblob.wizardry.registry.Spells#none WizardryRegistry.none}*/ +public class None extends Spell { + + public None() { + super(Tier.BASIC, 0, Element.MAGIC, "none", SpellType.UTILITY, 0, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Oakflesh.java b/src/main/java/electroblob/wizardry/spell/Oakflesh.java new file mode 100644 index 00000000..cb2f5fd6 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Oakflesh.java @@ -0,0 +1,73 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class Oakflesh extends Spell { + + public Oakflesh() { + super(Tier.APPRENTICE, 20, Element.HEALING, "oakflesh", SpellType.DEFENCE, 50, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + caster.addPotionEffect(new PotionEffect(MobEffects.RESISTANCE, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 1, false, false)); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.5f, 0.4f); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(!caster.isPotionActive(MobEffects.RESISTANCE)){ + + caster.addPotionEffect(new PotionEffect(MobEffects.RESISTANCE, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 1, false, false)); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)caster.posY + caster.getEyeHeight() - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.5f, 0.4f); + } + } + + caster.playSound(WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Petrify.java b/src/main/java/electroblob/wizardry/spell/Petrify.java new file mode 100644 index 00000000..4dd4cf24 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Petrify.java @@ -0,0 +1,114 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryBlocks; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.tileentity.TileEntityStatue; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class Petrify extends Spell { + + private static final int baseDuration = 900; + + /** The NBT tag name for storing the petrified flag in the target's tag compound. Defined here in case it changes. */ + public static final String NBT_KEY = "petrified"; + + public Petrify() { + super(Tier.ADVANCED, 40, Element.SORCERY, "petrify", SpellType.ATTACK, 100, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + Vec3d look = caster.getLookVec(); + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLiving && !world.isRemote){ + + EntityLiving target = (EntityLiving) rayTrace.entityHit; + + if(target.deathTime > 0) return false; + + BlockPos pos = new BlockPos(target); + + target.extinguish(); + + //Short mobs such as spiders and pigs + if((target.height < 1.2 || target.isChild()) && WizardryUtilities.canBlockBeReplaced(world, pos)){ + world.setBlockState(pos, WizardryBlocks.petrified_stone.getDefaultState()); + if(world.getTileEntity(pos) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 1); + ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + } + target.getEntityData().setBoolean(NBT_KEY, true); + target.setDead(); + } + //Normal sized mobs like zombies and skeletons + else if(target.height < 2.5 && WizardryUtilities.canBlockBeReplaced(world, pos) && WizardryUtilities.canBlockBeReplaced(world, pos.up())){ + world.setBlockState(pos, WizardryBlocks.petrified_stone.getDefaultState()); + if(world.getTileEntity(pos) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 2); + ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + } + + world.setBlockState(pos.up(), WizardryBlocks.petrified_stone.getDefaultState()); + if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(target, 2, 2); + } + target.getEntityData().setBoolean(NBT_KEY, true); + target.setDead(); + } + //Tall mobs like endermen + else if(WizardryUtilities.canBlockBeReplaced(world, pos) && WizardryUtilities.canBlockBeReplaced(world, pos.up()) && WizardryUtilities.canBlockBeReplaced(world, pos.up(2))){ + world.setBlockState(pos, WizardryBlocks.petrified_stone.getDefaultState()); + if(world.getTileEntity(pos) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 3); + ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + } + + world.setBlockState(pos.up(), WizardryBlocks.petrified_stone.getDefaultState()); + if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(target, 2, 3); + } + + world.setBlockState(pos.up(2), WizardryBlocks.petrified_stone.getDefaultState()); + if(world.getTileEntity(pos.up(2)) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(pos.up(2))).setCreatureAndPart(target, 3, 3); + } + target.getEntityData().setBoolean(NBT_KEY, true); + target.setDead(); + } + } + if(world.isRemote){ + for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + // I figured it out! when on client side, entityplayer.posY is at the eyes, not the feet! + double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + //world.spawnParticle("mobSpell", x1, y1, z1, -1*look.xCoord, -1*look.yCoord, -1*look.zCoord); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.1f, 0.1f); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.2f, 0.2f, 0.2f); + } + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/PhaseStep.java b/src/main/java/electroblob/wizardry/spell/PhaseStep.java new file mode 100644 index 00000000..a99034ce --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/PhaseStep.java @@ -0,0 +1,80 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class PhaseStep extends Spell { + + public PhaseStep() { + super(Tier.ADVANCED, 35, Element.SORCERY, "phase_step", SpellType.UTILITY, 40, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + // Phase step does not gain range from range multiplier, instead it increases the thickness + // of the wall you can teleport through. + RayTraceResult rayTrace = WizardryUtilities.rayTrace(5, world, caster, false); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + + BlockPos pos = new BlockPos(rayTrace.getBlockPos().getX(), (int)caster.posY, rayTrace.getBlockPos().getZ()); + + // The maximum wall thickness as determined by the range multiplier. The + 0.5f is so that + // weird float processing doesn't incorrectly round it down. + int maxThickness = 1 + (int)((modifiers.get(WizardryItems.range_upgrade) - 1)/Constants.RANGE_INCREASE_PER_LEVEL + 0.5f); + + if(rayTrace.sideHit.getAxis().isHorizontal()){ + + // i represents how far the player needs to teleport to get through the wall + for(int i = 0; i <= maxThickness; i++){ + + BlockPos pos1 = pos.offset(rayTrace.sideHit.getOpposite(), i); + + // Prevents the player from teleporting through unbreakable blocks, so they cannot cheat in other mods' mazes and dungeons. + if((WizardryUtilities.isBlockUnbreakable(world, pos1) || WizardryUtilities.isBlockUnbreakable(world, pos1.up())) && !Wizardry.settings.teleportThroughUnbreakableBlocks) return false; + + if(!world.getBlockState(pos1).getMaterial().blocksMovement() && !world.getBlockState(pos1.up()).getMaterial().blocksMovement()){ + + if(!world.isRemote){ + caster.setPositionAndUpdate(pos1.getX() + 0.5, caster.posY, pos1.getZ() + 0.5); + } + + caster.swingArm(hand); + return true; + } + } + } + } + + // This is here because the conditions are false on the client for whatever reason. (see the Javadoc for cast() + // for an explanation) + if(world.isRemote){ + for(int i=0;i<10;i++){ + double dx1 = caster.posX; + double dy1 = WizardryUtilities.getPlayerEyesPos(caster) - 1.5 + 2*world.rand.nextFloat(); + double dz1 = caster.posZ; + world.spawnParticle(EnumParticleTypes.PORTAL, dx1, dy1, dz1, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); + } + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); + } + + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java b/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java new file mode 100644 index 00000000..252868a7 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java @@ -0,0 +1,72 @@ +package electroblob.wizardry.spell; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.world.World; + +public class PlagueOfDarkness extends Spell { + + public PlagueOfDarkness() { + super(Tier.MASTER, 75, Element.NECROMANCY, "plague_of_darkness", SpellType.ATTACK, 200, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + List targets = WizardryUtilities.getEntitiesWithinRadius(5.0d*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world); + + for(EntityLivingBase target : targets){ + if(WizardryUtilities.isValidTarget(caster, target) && !MagicDamage.isEntityImmune(DamageType.WITHER, target)){ + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.WITHER), 8.0f * modifiers.get(SpellModifiers.DAMAGE)); + target.addPotionEffect(new PotionEffect(MobEffects.WITHER, (int)(140*modifiers.get(WizardryItems.duration_upgrade)), 2)); + } + } + if(world.isRemote){ + double particleX, particleZ; + for(int i=0;i<40*modifiers.get(WizardryItems.blast_upgrade);i++){ + particleX = caster.posX - 1.0d + 2*world.rand.nextDouble(); + particleZ = caster.posZ - 1.0d + 2*world.rand.nextDouble(); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, particleX, WizardryUtilities.getPlayerEyesPos(caster) - 1.5, particleZ, + particleX - caster.posX, 0, particleZ - caster.posZ, 0, 0.1f, 0.0f, 0.0f); + particleX = caster.posX - 1.0d + 2*world.rand.nextDouble(); + particleZ = caster.posZ - 1.0d + 2*world.rand.nextDouble(); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, particleX, WizardryUtilities.getPlayerEyesPos(caster) - 1.5, particleZ, + particleX - caster.posX, 0, particleZ - caster.posZ, 30, 0.1f, 0.0f, 0.05f); + particleX = caster.posX - 1.0d + 2*world.rand.nextDouble(); + particleZ = caster.posZ - 1.0d + 2*world.rand.nextDouble(); + + IBlockState block = WizardryUtilities.getBlockEntityIsStandingOn(caster); + + if(block != null){ + world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, caster.getEntityBoundingBox().minY, particleZ, + particleX - caster.posX, 0, particleZ - caster.posZ, Block.getStateId(block)); + } + } + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_DEATH, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/PocketFurnace.java b/src/main/java/electroblob/wizardry/spell/PocketFurnace.java new file mode 100644 index 00000000..76f70f58 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/PocketFurnace.java @@ -0,0 +1,72 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.FurnaceRecipes; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.world.World; + +public class PocketFurnace extends Spell { + + public PocketFurnace() { + super(Tier.APPRENTICE, 30, Element.FIRE, "pocket_furnace", SpellType.UTILITY, 40, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + int usesLeft = 5; + + ItemStack stack, result; + + for(int i=0; i 0; i++){ + + stack = caster.inventory.getStackInSlot(i); + + if(stack != null){ + + result = FurnaceRecipes.instance().getSmeltingResult(stack); + + if(result != null){ + if(stack.stackSize <= usesLeft){ + ItemStack stack2 = new ItemStack(result.getItem(), stack.stackSize, result.getItemDamage()); + if(WizardryUtilities.doesPlayerHaveItem(caster, result.getItem())){ + caster.inventory.addItemStackToInventory(stack2); + caster.inventory.setInventorySlotContents(i, null); + }else{ + caster.inventory.setInventorySlotContents(i, stack2); + } + usesLeft -= stack.stackSize; + }else{ + caster.inventory.decrStackSize(i, usesLeft); + caster.inventory.addItemStackToInventory(new ItemStack(result.getItem(), usesLeft, result.getItemDamage())); + usesLeft = 0; + } + } + } + } + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_FURNACE_FIRE_CRACKLE, 1, 0.75f); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + world.spawnParticle(EnumParticleTypes.FLAME, x1, y1, z1, 0, 0.01F, 0); + } + } + + return usesLeft < 5; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/PocketWorkbench.java b/src/main/java/electroblob/wizardry/spell/PocketWorkbench.java new file mode 100644 index 00000000..bcb5ce53 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/PocketWorkbench.java @@ -0,0 +1,40 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryGuiHandler; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class PocketWorkbench extends Spell { + + public PocketWorkbench() { + super(Tier.APPRENTICE, 30, Element.SORCERY, "pocket_workbench", SpellType.UTILITY, 40, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + // TODO: Investigate possible item duplication bug with this spell. So far I have been unable to recreate it. + if(!world.isRemote){ + caster.openGui(Wizardry.instance, WizardryGuiHandler.PORTABLE_CRAFTING, world, (int)caster.posX, (int)caster.posY, (int)caster.posZ); + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1, 1); + + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Poison.java b/src/main/java/electroblob/wizardry/spell/Poison.java new file mode 100644 index 00000000..9a69bc38 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Poison.java @@ -0,0 +1,108 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +public class Poison extends Spell { + + public Poison() { + super(Tier.APPRENTICE, 10, Element.EARTH, "poison", SpellType.ATTACK, 20, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + Vec3d look = caster.getLookVec(); + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; + // Has no effect on undead or spiders. + if(MagicDamage.isEntityImmune(DamageType.POISON, target)){ + if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + }else{ + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.POISON), 1.0f * modifiers.get(SpellModifiers.DAMAGE)); + target.addPotionEffect(new PotionEffect(MobEffects.POISON, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 1)); + } + } + + if(world.isRemote){ + for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + // I figured it out! when on client side, entityplayer.posY is at the eyes, not the feet! + double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + //world.spawnParticle("mobSpell", x1, y1, z1, -1*look.xCoord, -1*look.yCoord, -1*look.zCoord); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.3f, 0.7f, 0.0f); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.1f, 0.4f, 0.0f); + } + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + // Has no effect on undead or spiders. + if(!MagicDamage.isEntityImmune(DamageType.POISON, target) && !world.isRemote){ + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.POISON), 1.0f * modifiers.get(SpellModifiers.DAMAGE)); + target.addPotionEffect(new PotionEffect(MobEffects.POISON, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 1)); + } + + if(world.isRemote){ + + double dx = (target.posX - caster.posX)/caster.getDistanceToEntity(target); + double dy = (target.posY - caster.posY)/caster.getDistanceToEntity(target); + double dz = (target.posZ - caster.posZ)/caster.getDistanceToEntity(target); + + for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + + double x1 = caster.posX + dx*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = caster.posY + caster.getEyeHeight() - 0.4f + dy*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + dz*i/2 + world.rand.nextFloat()/5 - 0.1f; + + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.3f, 0.7f, 0.0f); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.1f, 0.4f, 0.0f); + } + } + + caster.swingArm(hand); + caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/PoisonBomb.java b/src/main/java/electroblob/wizardry/spell/PoisonBomb.java new file mode 100644 index 00000000..3ee00729 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/PoisonBomb.java @@ -0,0 +1,66 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.projectile.EntityPoisonBomb; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class PoisonBomb extends Spell { + + public PoisonBomb() { + super(Tier.APPRENTICE, 15, Element.EARTH, "poison_bomb", SpellType.ATTACK, 25, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + EntityPoisonBomb poisonbomb = new EntityPoisonBomb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + world.spawnEntityInWorld(poisonbomb); + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + EntityPoisonBomb poisonbomb = new EntityPoisonBomb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + poisonbomb.directTowards(target, 1.5f); + world.spawnEntityInWorld(poisonbomb); + } + + caster.swingArm(hand); + caster.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/ReplenishHunger.java b/src/main/java/electroblob/wizardry/spell/ReplenishHunger.java new file mode 100644 index 00000000..bf9983a6 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/ReplenishHunger.java @@ -0,0 +1,44 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class ReplenishHunger extends Spell { + + public ReplenishHunger() { + super(Tier.APPRENTICE, 10, Element.HEALING, "replenish_hunger", SpellType.UTILITY, 30, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(caster.getFoodStats().needFood()){ + int foodAmount = (int)(6*modifiers.get(SpellModifiers.DAMAGE)); + // Fixed issue #6: Changed to addStats, since setFoodLevel is client-side only + caster.getFoodStats().addStats(foodAmount, foodAmount*0.1f); + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 0.7f, 0.3f); + } + } + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/RingOfFire.java b/src/main/java/electroblob/wizardry/spell/RingOfFire.java new file mode 100644 index 00000000..e4f1bd84 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/RingOfFire.java @@ -0,0 +1,68 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.construct.EntityFireRing; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class RingOfFire extends Spell { + + public RingOfFire() { + super(Tier.ADVANCED, 30, Element.FIRE, "ring_of_fire", SpellType.ATTACK, 100, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(caster.onGround){ + if(!world.isRemote){ + EntityFireRing firering = new EntityFireRing(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(firering); + } + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1); + return true; + } + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + if(caster.onGround && world.getEntitiesWithinAABB(EntityFireRing.class, caster.getEntityBoundingBox()).isEmpty()){ + if(!world.isRemote){ + EntityFireRing firering = new EntityFireRing(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(firering); + } + + caster.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1); + return true; + } + return false; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/ShadowWard.java b/src/main/java/electroblob/wizardry/spell/ShadowWard.java new file mode 100644 index 00000000..b521db6e --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/ShadowWard.java @@ -0,0 +1,39 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.world.World; + +public class ShadowWard extends Spell { + + public ShadowWard() { + super(Tier.ADVANCED, 10, Element.NECROMANCY, "shadow_ward", SpellType.DEFENCE, 0, EnumAction.BLOCK, true); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(world.isRemote){ + double dx = -1 + 2*world.rand.nextFloat(); + double dy = -1 + world.rand.nextFloat(); + double dz = -1 + 2*world.rand.nextFloat(); + world.spawnParticle(EnumParticleTypes.PORTAL, caster.posX, WizardryUtilities.getPlayerEyesPos(caster), caster.posZ, dx, dy, dz); + } + + if(ticksInUse % 50 == 0){ + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_PORTAL_AMBIENT, 0.6f, 1.5f); + } + + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Shield.java b/src/main/java/electroblob/wizardry/spell/Shield.java new file mode 100644 index 00000000..518cbe22 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Shield.java @@ -0,0 +1,42 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.EntityShield; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class Shield extends Spell { + + public Shield() { + super(Tier.APPRENTICE, 5, Element.HEALING, "shield", SpellType.DEFENCE, 0, EnumAction.BLOCK, true); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + caster.addPotionEffect(new PotionEffect(MobEffects.RESISTANCE, 10, 0, false, false)); + + if(WizardData.get(caster).shield == null){ + WizardData.get(caster).shield = new EntityShield(world, caster); + if(!world.isRemote){ + world.spawnEntityInWorld(WizardData.get(caster).shield); + } + } + if(ticksInUse == 0){ + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); + } + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Shockwave.java b/src/main/java/electroblob/wizardry/spell/Shockwave.java new file mode 100644 index 00000000..1f48a728 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Shockwave.java @@ -0,0 +1,99 @@ +package electroblob.wizardry.spell; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.EnumAction; +import net.minecraft.network.play.server.SPacketEntityVelocity; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.world.World; + +public class Shockwave extends Spell { + + public Shockwave() { + super(Tier.MASTER, 65, Element.SORCERY, "shockwave", SpellType.ATTACK, 150, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + List targets = WizardryUtilities.getEntitiesWithinRadius(5.0d*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world); + + for(EntityLivingBase target : targets){ + if(WizardryUtilities.isValidTarget(caster, target)){ + // Damage increases closer to player up to a maximum of 4 hearts (at 1 block distance). + float damage = Math.min(8.0f/target.getDistanceToEntity(caster), 8.0f); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.BLAST), damage * modifiers.get(SpellModifiers.DAMAGE)); + + if(!world.isRemote){ + + // Entity speed increases closer to the player to a maximum of 3 (at 1 block distance). + // This is the entity's speed compared to its distance from the player. Used for a similar triangles + // based x, y and z speed calculation. + double velocityFactor = Math.min(5/target.getDistanceSqToEntity(caster), 3.0d); + + double dx = target.posX - caster.posX; + double dy = target.posY + 1 - caster.posY; + double dz = target.posZ - caster.posZ; + + target.motionX = velocityFactor * dx; + target.motionY = velocityFactor * dy; + target.motionZ = velocityFactor * dz; + + // Player motion is handled on that player's client so needs packets + if(target instanceof EntityPlayerMP){ + ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + } + } + } + } + if(world.isRemote){ + + double particleX, particleZ; + for(int i=0;i<40;i++){ + particleX = caster.posX - 1.0d + 2*world.rand.nextDouble(); + particleZ = caster.posZ - 1.0d + 2*world.rand.nextDouble(); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, particleX, WizardryUtilities.getPlayerEyesPos(caster) - 1.5, particleZ, + particleX - caster.posX, 0, particleZ - caster.posZ, 30, 0.8f, 0.8f, 1.0f); + particleX = caster.posX - 1.0d + 2*world.rand.nextDouble(); + particleZ = caster.posZ - 1.0d + 2*world.rand.nextDouble(); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, particleX, WizardryUtilities.getPlayerEyesPos(caster) - 1.5, particleZ, + particleX - caster.posX, 0, particleZ - caster.posZ, 30, 0.9f, 0.9f, 0.9f); + particleX = caster.posX - 1.0d + 2*world.rand.nextDouble(); + particleZ = caster.posZ - 1.0d + 2*world.rand.nextDouble(); + + IBlockState block = WizardryUtilities.getBlockEntityIsStandingOn(caster); + + if(block != null){ + world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, caster.getEntityBoundingBox().minY, particleZ, + particleX - caster.posX, 0, particleZ - caster.posZ, Block.getStateId(block)); + } + } + + world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, caster.posX, caster.getEntityBoundingBox().minY + 0.1, caster.posZ, 0, 0, 0); + + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SHOCKWAVE, 1.0f, 0.7f); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SHOCKWAVE, 2.0f, 0.3f); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/SilverfishSwarm.java b/src/main/java/electroblob/wizardry/spell/SilverfishSwarm.java new file mode 100644 index 00000000..bd7ae7bc --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SilverfishSwarm.java @@ -0,0 +1,47 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntitySilverfishMinion; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SilverfishSwarm extends Spell { + + public SilverfishSwarm() { + super(Tier.MASTER, 80, Element.EARTH, "silverfish_swarm", SpellType.MINION, 300, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + for(int i=0;i<20;i++){ + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 3, 6); + // The spell instantly fails if no space was found (see javadoc for the above method). + if(pos == null) return false; + + EntitySilverfishMinion silverfish = new EntitySilverfishMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(silverfish); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_FIRE_EXTINGUISH, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + // Can't possibly get this far if nothing was spawned. + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/SixthSense.java b/src/main/java/electroblob/wizardry/spell/SixthSense.java new file mode 100644 index 00000000..f6505a75 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SixthSense.java @@ -0,0 +1,41 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class SixthSense extends Spell { + + public SixthSense() { + super(Tier.APPRENTICE, 20, Element.EARTH, "sixth_sense", SpellType.UTILITY, 100, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + // Cannot be cast when it has already been cast + if(!world.isRemote){ + caster.addPotionEffect(new PotionEffect(WizardryPotions.sixth_sense, (int)(400*modifiers.get(WizardryItems.duration_upgrade)), (int)((modifiers.get(WizardryItems.range_upgrade)-1f)/Constants.RANGE_INCREASE_PER_LEVEL))); + } + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SHOOT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Slime.java b/src/main/java/electroblob/wizardry/spell/Slime.java new file mode 100644 index 00000000..6d9d5346 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Slime.java @@ -0,0 +1,117 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntityMagicSlime; +import electroblob.wizardry.registry.WizardryAchievements; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntitySkeleton; +import net.minecraft.entity.monster.EntitySlime; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +public class Slime extends Spell { + + public Slime() { + super(Tier.ADVANCED, 20, Element.EARTH, "slime", SpellType.ATTACK, 50, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + Vec3d look = caster.getLookVec(); + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){ + + EntityLivingBase target = (EntityLivingBase)rayTrace.entityHit; + + if(target instanceof EntitySlime){ + if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + }else if(!(target instanceof EntityMagicSlime)){ + + if(target instanceof EntitySkeleton) caster.addStat(WizardryAchievements.slime_skeleton); + + if(!world.isRemote){ + EntityMagicSlime slime = new EntityMagicSlime(world, caster, target, (int)(200*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(slime); + } + } + } + + if(world.isRemote){ + for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + + world.spawnParticle(EnumParticleTypes.SLIME, x1, y1, z1, 0.0d, 0.0d, 0.0d); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.2f, 0.8f, 0.1f); + } + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_SLIME_ATTACK, 1.0F, 0.5F); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null && !(target instanceof EntitySlime) && !(target instanceof EntityMagicSlime)){ + + if(!world.isRemote){ + EntityMagicSlime slime = new EntityMagicSlime(world, caster, target, (int)(200*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(slime); + } + + if(world.isRemote){ + + double dx = (target.posX - caster.posX)/caster.getDistanceToEntity(target); + double dy = (target.posY - caster.posY)/caster.getDistanceToEntity(target); + double dz = (target.posZ - caster.posZ)/caster.getDistanceToEntity(target); + + for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + + double x1 = caster.posX + dx*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = caster.posY + caster.getEyeHeight() - 0.4f + dy*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + dz*i/2 + world.rand.nextFloat()/5 - 0.1f; + + world.spawnParticle(EnumParticleTypes.SLIME, x1, y1, z1, 0.0d, 0.0d, 0.0d); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.2f, 0.8f, 0.1f); + } + } + + caster.swingArm(hand); + caster.playSound(SoundEvents.ENTITY_SLIME_ATTACK, 1.0F, 0.5F); + caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + return true; + + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/SmokeBomb.java b/src/main/java/electroblob/wizardry/spell/SmokeBomb.java new file mode 100644 index 00000000..6a71a532 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SmokeBomb.java @@ -0,0 +1,66 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.projectile.EntitySmokeBomb; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class SmokeBomb extends Spell { + + public SmokeBomb() { + super(Tier.BASIC, 10, Element.FIRE, "smoke_bomb", SpellType.ATTACK, 20, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + EntitySmokeBomb smokebomb = new EntitySmokeBomb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + world.spawnEntityInWorld(smokebomb); + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + EntitySmokeBomb smokebomb = new EntitySmokeBomb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + smokebomb.directTowards(target, 1.5f); + world.spawnEntityInWorld(smokebomb); + } + + caster.swingArm(hand); + caster.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Snare.java b/src/main/java/electroblob/wizardry/spell/Snare.java new file mode 100644 index 00000000..6d1b4815 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Snare.java @@ -0,0 +1,66 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryBlocks; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.tileentity.TileEntityPlayerSave; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class Snare extends Spell { + + public Snare() { + super(Tier.BASIC, 10, Element.EARTH, "snare", SpellType.ATTACK, 10, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.rayTrace(10*modifiers.get(WizardryItems.range_upgrade), world, caster, true); + + // Gets block the player is looking at and places snare + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + + BlockPos pos = rayTrace.getBlockPos(); + + if(rayTrace.sideHit == EnumFacing.UP && world.isSideSolid(pos, EnumFacing.UP) && WizardryUtilities.canBlockBeReplaced(world, pos.up())){ + + if(!world.isRemote){ + world.setBlockState(pos.up(), WizardryBlocks.snare.getDefaultState()); + ((TileEntityPlayerSave)world.getTileEntity(pos.up())).setCaster(caster); + } + + double dx = pos.getX() + 0.5 - caster.posX; + double dy = pos.getY() + 1.5 - (caster.posY + caster.height/2); + double dz = pos.getZ() + 0.5 - caster.posZ; + + if(world.isRemote){ + for(int i=1;i<5;i++){ + float brightness = world.rand.nextFloat()/4; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0.0d, 0.0d, 0.0d, 20 + world.rand.nextInt(8), brightness, brightness + 0.1f, 0.0f); + Wizardry.proxy.spawnParticle(WizardryParticleType.LEAF, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, -0.01, 0, 40 + world.rand.nextInt(10)); + } + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_GRASS_PLACE, 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); + return true; + } + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Snowball.java b/src/main/java/electroblob/wizardry/spell/Snowball.java new file mode 100644 index 00000000..c5aed5ab --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Snowball.java @@ -0,0 +1,41 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.projectile.EntitySnowball; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class Snowball extends Spell { + + public Snowball() { + super(Tier.BASIC, 1, Element.ICE, "snowball", SpellType.ATTACK, 1, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + EntitySnowball snowball = new EntitySnowball(world, caster); + snowball.setHeadingFromThrower(caster, caster.rotationPitch, caster.rotationYaw, 0.0f, 1.5f, 1.0f); + world.spawnEntityInWorld(snowball); + } + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + caster.swingArm(hand); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/SparkBomb.java b/src/main/java/electroblob/wizardry/spell/SparkBomb.java new file mode 100644 index 00000000..e0c55295 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SparkBomb.java @@ -0,0 +1,66 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.projectile.EntitySparkBomb; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class SparkBomb extends Spell { + + public SparkBomb() { + super(Tier.APPRENTICE, 15, Element.LIGHTNING, "spark_bomb", SpellType.ATTACK, 25, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + EntitySparkBomb sparkBomb = new EntitySparkBomb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + world.spawnEntityInWorld(sparkBomb); + } + + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + EntitySparkBomb sparkBomb = new EntitySparkBomb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + sparkBomb.directTowards(target, 1.5f); + world.spawnEntityInWorld(sparkBomb); + } + + caster.swingArm(hand); + caster.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/SpectralPathway.java b/src/main/java/electroblob/wizardry/spell/SpectralPathway.java new file mode 100644 index 00000000..f544ffb3 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SpectralPathway.java @@ -0,0 +1,80 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryBlocks; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.tileentity.TileEntityTimer; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumFacing.AxisDirection; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SpectralPathway extends Spell { + + public SpectralPathway() { + super(Tier.ADVANCED, 40, Element.SORCERY, "spectral_pathway", SpellType.UTILITY, 300, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + // Won't work if caster is airborne or if they are already on a bridge (prevents infinite bridges) + if(WizardryUtilities.getBlockEntityIsStandingOn(caster).getBlock() == Blocks.AIR + || WizardryUtilities.getBlockEntityIsStandingOn(caster).getBlock() == WizardryBlocks.spectral_block){ + return false; + } + + EnumFacing direction = caster.getHorizontalFacing(); + + boolean flag = false; + + if(!world.isRemote){ + + int baseLength = 15; + + // Gets the coordinates of the nearest block intersection to the player's feet. + // Remember that a block always takes the coordinates of its northwestern corner. + BlockPos origin = new BlockPos(Math.round(caster.posX), (int)caster.getEntityBoundingBox().minY-1, Math.round(caster.posZ)); + + int startPoint = direction.getAxisDirection() == AxisDirection.POSITIVE ? -1 : 0; + + for(int i=0; i<(int)(baseLength*modifiers.get(WizardryItems.range_upgrade)); i++){ + // If either a block gets placed or one has already been placed, flag is set to true. + flag = placePathwayBlockIfPossible(world, origin.offset(direction, startPoint + i), modifiers.get(WizardryItems.duration_upgrade)) || flag; + flag = placePathwayBlockIfPossible(world, origin.offset(direction, startPoint + i) + // Moves the BlockPos minus one block perpendicular to direction. + .offset(EnumFacing.getFacingFromAxis(AxisDirection.NEGATIVE, direction.rotateY().getAxis())), modifiers.get(WizardryItems.duration_upgrade)) || flag; + } + } + // TODO: There may be some client/server discrepancies here. + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION_LARGE, 1.0f, 1.0f); + + return flag; + } + + private static boolean placePathwayBlockIfPossible(World world, BlockPos pos, float durationMultiplier){ + if(WizardryUtilities.canBlockBeReplacedB(world, pos)){ + world.setBlockState(pos, WizardryBlocks.spectral_block.getDefaultState()); + if(world.getTileEntity(pos) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(pos)).setLifetime((int)(1200*durationMultiplier)); + } + return true; + } + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Spell.java b/src/main/java/electroblob/wizardry/spell/Spell.java new file mode 100644 index 00000000..594f1ef2 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Spell.java @@ -0,0 +1,424 @@ +package electroblob.wizardry.spell; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntityWizard; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.fml.common.registry.IForgeRegistry; +import net.minecraftforge.fml.common.registry.IForgeRegistryEntry; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** + * Generic spell class which is the superclass to all spells in wizardry. When extending this class, you must do + * the following: + *

    + * - Have a constructor which passes all necessary constants into the super constructor. I define the constants here + * so that the constructor for an individual spell has no parameters, but you may prefer to pass in the parameters when + * the spell is registered, so all the mana costs etc. are in one place like a sort of sandbox. + *

    + * - Implement the {@link Spell#cast(World, EntityPlayer, EnumHand, int, SpellModifiers)} method, in which you should + * execute the code that makes the spell work, and return true or false depending on whether the spell succeeded and + * therefore whether mana should be used up. + *

    + * - Register the spell using {@link RegistryEvent.Register}, with {@link Spell} as the type parameter. Each spell + * should have a single instance, like blocks and items. + * As of Wizardry 2.1, spells use the Forge registry system. Related methods such as {@link Spell#id()} and + * {@link Spell#get(int)} have been re-routed to use this system, leaving minimal external changes. Note also that + * the constructor automatically sets the registry name for you, though you may change it afterwards if necessary. + *

    + * Also note that you can override some other methods from this class. For example, to add a + * specific kind of formatting to a spell name or description, you can override {@link Spell#getDisplayName()}, + * {@link Spell#getDisplayNameWithFormatting()} or {@link Spell#getDescription()} and append the formatting code (though + * you will have to call super() to get the name itself, since the unlocalised name is private). See + * {@link SummonShadowWraith#getDescription()} for an example. + *


    + * This class is also home to some useful static methods for interacting with the spell registry:

    + * {@link Spell#get(int)} gets a spell instance from its integer id, which corresponds to the metadata of its spell book.
    + * {@link Spell#get(String)} gets a spell instance from its unlocalised name.
    + * {@link Spell#getSpells(Predicate)} returns a list of spell instances the match the given {@link Predicate}.
    + * {@link Spell#getTotalSpellCount()} returns the total number of registered spells. + *


    + * Spell implements the {@link Comparable} interface, and as such, any collection of spells can be sorted. Spells are + * sorted by increasing tier (see {@link Tier}), from novice to master. Within each tier, spells are sorted by element, + * the order of which is as defined in {@link Element} (i.e. magic, fire, ice, lightning, necromancy, earth, sorcery, healing). + *
    + * @since Wizardry 1.0 + * @see electroblob.wizardry.item.ItemSpellBook ItemSpellBook + * @see electroblob.wizardry.item.ItemScroll ItemScroll + * @see Spells + */ +public abstract class Spell extends IForgeRegistryEntry.Impl implements Comparable { + + /** Forge registry-based replacement for the internal spells list. */ + public static IForgeRegistry registry; + + /** The tier this spell belongs to. */ + public final Tier tier; + /** Mana cost of the spell. If it is a continuous spell the cost is per second. */ + public final int cost; + /** The element this spell belongs to. */ + public final Element element; + /** The unlocalised name of the spell. */ + private final String unlocalisedName; + /** The type of spell this is classified as. */ + public final SpellType type; + /** Cooldown for the spell in ticks */ + public final int cooldown; + /** The action the player does when this spell is cast. */ + public final EnumAction action; + /** Whether or not the spell is continuous (keeps going as long as the mouse button is held) */ + public final boolean isContinuous; + + /** ResourceLocation of the spell icon. */ + private final ResourceLocation icon; + /** Mod ID of the mod that added this spell; defaults to {@link Wizardry#MODID} if not specified. */ + private final String modID; + + /** False if the spell has been disabled in the config file, true otherwise. This is now encapsulated to stop it + * being fiddled with. */ + private boolean isEnabled = true; + + /** + * This constructor should be called from any subclasses, either feeding in the constants directly or through + * their own constructor from wherever the spell is registered. This is the constructor for wizardry's own spells; + * spells added by other mods should use {@link Spell#Spell(Tier, int, Element, String, SpellType, int, EnumAction, boolean, String)}. + * @param tier The tier this spell belongs to. + * @param cost The amount of mana used to cast the spell. If this is a continuous spell, it represents mana cost + * per second and should be a multiple of 5. + * @param element The element this spell belongs to. + * @param name The registry name of the spell. This will also be the name of the icon file. The spell's + * unlocalised name will be a resource location with the format [modid]:[name]. + * @param cooldown The cooldown time for this spell in ticks. + * @param action The vanilla usage action to be displayed when casting this spell. + * @param isContinuous Whether this spell is continuous, meaning you cast it for a length of time by holding the + * right mouse button. + */ + public Spell(Tier tier, int cost, Element element, String name, SpellType type, int cooldown, EnumAction action, boolean isContinuous){ + this(tier, cost, element, name, type, cooldown, action, isContinuous, Wizardry.MODID); + } + + /** + * This constructor should be called from any subclasses, either feeding in the constants directly or through + * their own constructor from wherever the spell is registered. + * @param tier The tier this spell belongs to. + * @param cost The amount of mana used to cast the spell. If this is a continuous spell, it represents mana cost + * per second and should be a multiple of 5. + * @param element The element this spell belongs to. + * @param name The registry name of the spell, excluding the mod id. This will also be the name of the icon + * file. The spell's unlocalised name will be a resource location with the format [modid]:[name]. + * @param cooldown The cooldown time for this spell in ticks. + * @param action The vanilla usage action to be displayed when casting this spell (see {@link}EnumAction) + * @param isContinuous Whether this spell is continuous, meaning you cast it for a length of time by holding the + * right mouse button. + * @param modID The mod id of the mod that added this spell. This allows wizardry to use the correct file + * path for the spell icon, and also more generally to distinguish between original and addon spells. + */ + public Spell(Tier tier, int cost, Element element, String name, SpellType type, int cooldown, EnumAction action, boolean isContinuous, String modID){ + this.tier = tier; + this.cost = cost; + this.element = element; + this.type = type; + this.cooldown = cooldown; + this.action = action; + this.isContinuous = isContinuous; + this.modID = modID; + this.setRegistryName(modID, name); + this.unlocalisedName = this.getRegistryName().toString(); + this.icon = new ResourceLocation(this.modID, "textures/spells/" + name + ".png"); + } + + /** + * Casts the spell. Each subclass must override this method and within it execute the code to make the spell work. + * Returns a boolean so that the main onItemRightClick or onUsingItemTick method can check if the spell + * was actually cast or whether a spell specific condition caused it not to be (for example, heal won't work if the player is on + * full health), preventing unfair drain of mana. + *

    + * Each spell must return true when it works or the spell will not use up mana. Note that (!world.isRemote) does not count + * as a condition; return true should be outside it - in other words, return a value on both the client and the server. + *

    + * It's worth noting that on the client side, this method only gets called if the server side + * cast() method succeeded, so you can put any particle spawning code outside of any success conditions if there + * are discrepancies between client and server. + * @param world A reference to the world object. Again this is for convenience, you can also use caster.worldObj. + * @param caster The EntityPlayer that cast the spell. + * @param hand The hand that is holding the item used to cast the spell. If no item was used, this will be the main hand. + * @param ticksInUse The number of ticks the spell has already been cast for. For all non-continuous spells, this is 0 and + * is not used. For continuous spells, it is passed in as the maximum use duration of the item minus the count parameter in + * onUsingItemTick and therefore it increases by 1 each tick. + * @param modifiers A {@link SpellModifiers} object containing the modifiers that have been applied to the spell. See + * the javadoc for that class for more information. If no modifiers are required, pass in {@code new SpellModifiers()}. + * @return True if the spell succeeded and mana should be used up, false if not. + */ + public abstract boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers); + + /** Casts the spell, but with an EntityLiving as the caster. Each subclass can optionally override this method and + * within it execute the code to make the spell work. Returns a boolean to allow whatever calls this method to check if the spell + * was actually cast or whether a spell specific condition caused it not to be (for example, heal won't work if the caster is on + * full health). + *

    + * This method is intended for use by NPCs (see {@link EntityWizard}) so that they can cast spells. Override it if you want + * a spell to be cast by wizards. Note that you must also override {@link Spell#canBeCastByNPCs()} to return true to allow wizards + * to select the spell. For some spells, this method may well be exactly the same as the regular cast method; for + * others it won't be - for example, projectile-based spells are normally done using the player's look vector, but + * NPCs need to use a target-based method instead. + *

    + * Each spell must return true when it works. Note that (!world.isRemote) does not count as a condition; return true should be outside + * it - in other words, return a value on both the client and the server. + *

    + * It's worth noting that on the client side, this method only gets called if the server side + * cast() method succeeded, so you can put any particle spawning code outside of any success conditions if there + * are discrepancies between client and server. + * @param world A reference to the world object. This is for convenience, you can also use caster.worldObj. + * @param caster The EntityLiving that cast the spell. + * @param hand The hand that is holding the item used to cast the spell. This will almost certainly be the main hand. + * @param ticksInUse The number of ticks the spell has already been cast for. For all non-continuous spells, this is 0 and + * is not used. + * @param target The EntityLivingBase that is targeted by the spell. May be null in some cases. + * @param modifiers A {@link SpellModifiers} object containing the modifiers that have been applied to the spell. See + * the javadoc for that class for more information. If no modifiers are required, pass in {@code new SpellModifiers()}. + * @return True if the spell succeeded, false if not. Returns false by default. + */ + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + return false; + } + + /** Whether NPCs such as wizards can cast this spell. If you have overridden {@link Spell#cast(World, EntityLiving, EnumHand, int, EntityLivingBase, SpellModifiers)}, + * you should override this to return true. */ + public boolean canBeCastByNPCs(){ + return false; + } + + /** Whether this spell requires a packet to be sent when it is cast. Returns true by default, but can be overridden + * to return false if the spell's cast() method does not use any code that must be executed client-side (i.e. + * particle spawning). Does nothing for continuous spells, because they never need to send packets. + *

    + * If in doubt, leave this method as is; it is purely an optimisation. + * @return false if the spell code should only be run on the server and the client of the player casting it
    + * true if the spell code should be run on the server and all clients in the dimension */ + // Edit: Turns out that swingItem() actually sends packets to all nearby clients, but not the client doing the swinging. + // Also, now I think about it, this method isn't going to make the slightest bit of difference to the item usage + // actions since setItemInUse() is called in ItemWand, not the spell class - so the only thing that matters here is + // the particles. + public boolean doesSpellRequirePacket(){ + return true; + } + + /** Returns this spell's id number, which now corresponds to its position in the spell registry. + * Returns -1 if the spell has not been registered. */ + // This is final so nothing can override it, because that would cause all kinds of problems! + public final int id(){ + return registry.getValues().indexOf(this); + } + + /** Returns the mod ID for this spell, which should be the ID of the mod that added it. The mod ID is + * used to tell wizardry which filepath to use for the spell's icon. As of Wizardry 1.2, the field itself is + * private, but this getter is provided for external use (though it is never called in the main Wizardry mod). */ + public final String getModID(){ + return modID; + } + + /** Returns the ResourceLocation for this spell's icon. */ + public final ResourceLocation getIcon(){ + return icon; + } + + /** Returns the unlocalised name of the spell, without any prefixes or suffixes, e.g. "flame_ray". This should + * only be used for translation purposes. */ + public final String getUnlocalisedName(){ + return unlocalisedName; + } + + /* The general idea with translation is to use net.minecraft.client.resources.I18n directly on the client side (and + * just prepend formatting codes where necessary), and to use TextComponentTranslation on the server (setting the + * style as necessary). TextComponentTranslation effectively stores what needs to be translated, without actually + * translating it. If, for whatever reason, you need to supply an ITextComponent but don't want it translated + * (perhaps a name?), you can use TextComponentString, which will simply keep the raw string it is given. */ + + /** Returns the translated display name of the spell, without formatting (i.e. not coloured). Client-side only! + * On the server side, use {@link TextComponentTranslation} (see {@link Spell#getNameForTranslation()}). */ + @SideOnly(Side.CLIENT) + public String getDisplayName(){ + return net.minecraft.client.resources.I18n.format("spell." + unlocalisedName); + } + + /** Returns a {@code TextComponentTranslation} which will be translated to the display name of the spell, without + * formatting (i.e. not coloured). */ + public TextComponentTranslation getNameForTranslation(){ + return new TextComponentTranslation("spell." + unlocalisedName); + } + + /** Returns the translated display name of the spell, with formatting (i.e. coloured). Client-side only! + * On the server side, use {@link TextComponentTranslation} (see {@link Spell#getNameForTranslationFormatted()}). */ + @SideOnly(Side.CLIENT) + public String getDisplayNameWithFormatting(){ + return this.element.getFormattingCode() + net.minecraft.client.resources.I18n.format("spell." + unlocalisedName); + } + + /** Returns a {@code TextComponentTranslation} which will be translated to the display name of the spell, with + * formatting (i.e. coloured). */ + public ITextComponent getNameForTranslationFormatted(){ + return new TextComponentTranslation("spell." + unlocalisedName).setStyle(this.element.getColour()); + } + + /** Returns the translated description of the spell, without formatting. Client-side only! You should not + * need to use this on the server side. */ + @SideOnly(Side.CLIENT) + public String getDescription(){ + return net.minecraft.client.resources.I18n.format("spell." + unlocalisedName + ".desc"); + } + + /** Returns whether the spell is enabled in the config. */ + public final boolean isEnabled() { + return isEnabled; + } + + /** Sets whether the spell is enabled or not. */ + public final void setEnabled(boolean isEnabled) { + this.isEnabled = isEnabled; + } + + // Spells are sorted according to tier and element. Where several spells have the same tier and element, + // they will remain in the order they were registered. + @Override + public final int compareTo(Spell spell){ + + if(this.tier.ordinal() > spell.tier.ordinal()){ + return 1; + }else if(this.tier.ordinal() < spell.tier.ordinal()){ + return -1; + }else{ + if(this.element.ordinal() > spell.element.ordinal()){ + return 1; + }else if(this.element.ordinal() < spell.element.ordinal()){ + return -1; + }else{ + return 0; + } + } + } + + // ================================================ Static methods ================================================== + + /** Returns the total number of registered spells, excluding the 'None' spell. Returns the same number that would + * be returned by Spell.getSpells(Spell.allSpells).size(), but this method is more efficient. */ + public static int getTotalSpellCount(){ + return registry.getValues().size() - 1; + } + + /** Gets a spell instance from its integer id, which now corresponds to its id in the spell registry. + * If the given id has no spell (i.e. is less than 0 or greater than the total number of spells - 1) + * then it will return the {@link None} spell. + *

    + * If you are calling this from inside a loop in which you are iterating through the spells, there is + * probably a better way; see {@link Spell#getSpells(Predicate)}. */ + public static Spell get(int id){ + if(id < 0 || id >= registry.getValues().size()){ + return Spells.none; + } + Spell spell = registry.getValues().get(id); + return spell == null ? Spells.none : spell; + } + + /** Returns the spell with the given registry name, or null if no such spell exists. + * @param name The registry name of the spell, in the form [mod id]:[spell name]. If no mod id is specified, it + * defaults to {@link Wizardry#MODID}. */ + public static Spell get(String name){ + ResourceLocation key = new ResourceLocation(name); + if(key.getResourceDomain().equals("minecraft")) key = new ResourceLocation(Wizardry.MODID, name); + return registry.getValue(key); + } + + /** Returns a list of all registered spells' registry names, excluding the 'none' spell. Used in commands. */ + public static String[] getSpellNames(){ + // Maybe it would be better to store all of this statically? + Set keys = new HashSet(registry.getKeys()); + keys.remove(registry.getKey(Spells.none)); + // Streams! + return keys.stream().map(ResourceLocation::toString).toArray(String[]::new); + } + + /** + * Returns a list containing all spells matching the given {@link Predicate}. The returned list is separate from the + * internal spells list; any changes you make to the returned list will have no effect on wizardry since the + * returned list is local to this method. Never includes the {@link None} spell. For convenience, there are some + * predefined predicates in the Spell class (some of these really aren't shortcuts any more): + *

    + * {@link Spell#allSpells} will allow all spells to be returned
    + * {@link Spell#enabledSpells} will filter out any spells that are disabled in the config
    + * {@link Spell#npcSpells} will only allow enabled spells that can be cast by NPCs (see {@link Spell#canBeCastByNPCs()})
    + * {@link Spell#nonContinuousSpells} will filter out continuous spells but not disabled spells
    + * {@link Spell.TierElementFilter} will only allow enabled spells of the specified tier and element + * + * @param filter A Predicate<Spell> that the returned spells must satisfy. + * + * @return A local, modifiable list of spells matching the given predicate. Note that this list may be empty. + * */ + public static List getSpells(Predicate filter){ + + ArrayList spells = new ArrayList(1); + + for(Spell spell : registry.getValues()){ + if(filter.test(spell) && spell != Spells.none) spells.add(spell); + } + + return spells; + } + + /** Predicate which allows all spells. */ + public static Predicate allSpells = s -> true; + + /** Predicate which allows all enabled spells. */ + public static Predicate enabledSpells = Spell::isEnabled; + + /** Predicate which allows all non-continuous spells, even those that have been disabled. */ + public static Predicate nonContinuousSpells = s -> !s.isContinuous; + + /** Predicate which allows all enabled spells for which {@link Spell#canBeCastByNPCs()} returns true. */ + public static Predicate npcSpells = s -> s.isEnabled() && s.canBeCastByNPCs(); + + /** Predicate which allows all enabled spells of the given tier and element (create an instance of this class + * each time you want to use it). This is somewhat useless now that Wizardry uses Java 8, but it is more readable + * than a lambda expression and you don't need to remember to check that the spell is enabled every time. */ + public static class TierElementFilter implements Predicate { + + private Tier tier; + private Element element; + + /** + * Creates a new TierElementFilter that checks for the given tier and element. Does not allow spells that have + * been disabled in the config. + * @param tier The EnumTier to check for. Pass in null to allow all tiers. + * @param element The EnumElement to check for. Pass in null to allow all elements. + */ + public TierElementFilter(Tier tier, Element element){ + this.tier = tier; + this.element = element; + } + + @Override + public boolean test(Spell spell) { + return spell.isEnabled() && (this.tier == null || spell.tier == this.tier) + && (this.element == null || spell.element == this.element); + } + }; +} diff --git a/src/main/java/electroblob/wizardry/spell/SpiderSwarm.java b/src/main/java/electroblob/wizardry/spell/SpiderSwarm.java new file mode 100644 index 00000000..a7c6a90a --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SpiderSwarm.java @@ -0,0 +1,72 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntitySpiderMinion; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SpiderSwarm extends Spell { + + public SpiderSwarm() { + super(Tier.ADVANCED, 45, Element.EARTH, "spider_swarm", SpellType.MINION, 200, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + for(int i=0;i<5;i++){ + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 3, 6); + // The spell instantly fails if no space was found (see javadoc for the above method). + if(pos == null) return false; + + EntitySpiderMinion spider = new EntitySpiderMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(spider); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_FIRE_EXTINGUISH, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + // Can't possibly get this far if nothing was spawned. + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + + if(!world.isRemote){ + for(int i=0;i<5;i++){ + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 3, 6); + // The spell instantly fails if no space was found (see javadoc for the above method). + if(pos == null) return false; + + EntitySpiderMinion spider = new EntitySpiderMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(spider); + } + } + + caster.playSound(SoundEvents.BLOCK_FIRE_EXTINGUISH, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + // Can't possibly get this far if nothing was spawned. + return true; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } +} diff --git a/src/main/java/electroblob/wizardry/spell/StaticAura.java b/src/main/java/electroblob/wizardry/spell/StaticAura.java new file mode 100644 index 00000000..851c260f --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/StaticAura.java @@ -0,0 +1,67 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class StaticAura extends Spell { + + public StaticAura() { + super(Tier.ADVANCED, 40, Element.LIGHTNING, "static_aura", SpellType.DEFENCE, 250, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + // Cannot be cast when it has already been cast + if(!caster.isPotionActive(WizardryPotions.static_aura)){ + if(!world.isRemote){ + caster.addPotionEffect(new PotionEffect(WizardryPotions.static_aura, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0)); + } + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); + return true; + } + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + + if(target != null){ + // Cannot be cast when it has already been cast + if(!caster.isPotionActive(WizardryPotions.static_aura)){ + if(!world.isRemote){ + caster.addPotionEffect(new PotionEffect(WizardryPotions.static_aura, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0)); + } + caster.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); + return true; + } + return false; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs() { + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonBlaze.java b/src/main/java/electroblob/wizardry/spell/SummonBlaze.java new file mode 100644 index 00000000..3882eae8 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonBlaze.java @@ -0,0 +1,67 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntityBlazeMinion; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SummonBlaze extends Spell { + + public SummonBlaze() { + super(Tier.ADVANCED, 40, Element.FIRE, "summon_blaze", SpellType.MINION, 200, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntityBlazeMinion blaze = new EntityBlazeMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(blaze); + } + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntityBlazeMinion blaze = new EntityBlazeMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(blaze); + } + + caster.playSound(SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonIceGiant.java b/src/main/java/electroblob/wizardry/spell/SummonIceGiant.java new file mode 100644 index 00000000..26e986f8 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonIceGiant.java @@ -0,0 +1,51 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntityIceGiant; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SummonIceGiant extends Spell { + + public SummonIceGiant() { + super(Tier.MASTER, 100, Element.ICE, "summon_ice_giant", SpellType.MINION, 400, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + if(!world.isRemote){ + + EntityIceGiant icegiant = new EntityIceGiant(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(icegiant); + } + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)pos.getX() + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)pos.getY() + 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)pos.getZ() + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.1F + 0.2F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonIceWraith.java b/src/main/java/electroblob/wizardry/spell/SummonIceWraith.java new file mode 100644 index 00000000..6f914344 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonIceWraith.java @@ -0,0 +1,66 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntityIceWraith; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SummonIceWraith extends Spell { + + public SummonIceWraith() { + super(Tier.ADVANCED, 40, Element.ICE, "summon_ice_wraith", SpellType.MINION, 200, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntityIceWraith iceWraith = new EntityIceWraith(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(iceWraith); + } + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntityIceWraith iceWraith = new EntityIceWraith(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(iceWraith); + } + caster.playSound(SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonIronGolem.java b/src/main/java/electroblob/wizardry/spell/SummonIronGolem.java new file mode 100644 index 00000000..4f510f4d --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonIronGolem.java @@ -0,0 +1,50 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.monster.EntityIronGolem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SummonIronGolem extends Spell { + + public SummonIronGolem() { + super(Tier.MASTER, 175, Element.SORCERY, "summon_iron_golem", SpellType.MINION, 400, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + if(!world.isRemote){ + EntityIronGolem golem = new EntityIronGolem(world); + golem.setPosition(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); + world.spawnEntityInWorld(golem); + } + + for(int i=0; i<10; i++){ + double x1 = (double)((float)pos.getX() + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)pos.getY() + 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)pos.getZ() + world.rand.nextFloat()*2 - 1.0F); + if(world.isRemote){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); + } + } + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonLightningWraith.java b/src/main/java/electroblob/wizardry/spell/SummonLightningWraith.java new file mode 100644 index 00000000..6c74adf1 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonLightningWraith.java @@ -0,0 +1,65 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntityLightningWraith; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SummonLightningWraith extends Spell { + + public SummonLightningWraith() { + super(Tier.ADVANCED, 40, Element.LIGHTNING, "summon_lightning_wraith", SpellType.MINION, 200, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntityLightningWraith lightningWraith = new EntityLightningWraith(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(lightningWraith); + } + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntityLightningWraith lightningWraith = new EntityLightningWraith(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(lightningWraith); + } + caster.playSound(SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonPhoenix.java b/src/main/java/electroblob/wizardry/spell/SummonPhoenix.java new file mode 100644 index 00000000..5dc24a07 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonPhoenix.java @@ -0,0 +1,46 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntityPhoenix; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SummonPhoenix extends Spell { + + public SummonPhoenix() { + super(Tier.MASTER, 150, Element.FIRE, "summon_phoenix", SpellType.MINION, 400, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntityPhoenix phoenix = new EntityPhoenix(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, + (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(phoenix); + } + + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonShadowWraith.java b/src/main/java/electroblob/wizardry/spell/SummonShadowWraith.java new file mode 100644 index 00000000..621cfeab --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonShadowWraith.java @@ -0,0 +1,51 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntityShadowWraith; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class SummonShadowWraith extends Spell { + + public SummonShadowWraith() { + super(Tier.MASTER, 100, Element.NECROMANCY, "summon_shadow_wraith", SpellType.MINION, 400, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntityShadowWraith shadowWraith = new EntityShadowWraith(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(shadowWraith); + } + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + @SideOnly(Side.CLIENT) + @Override + public String getDescription(){ + return "\u00A7k" + super.getDescription(); + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonSkeleton.java b/src/main/java/electroblob/wizardry/spell/SummonSkeleton.java new file mode 100644 index 00000000..7c086e9f --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonSkeleton.java @@ -0,0 +1,72 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntitySkeletonMinion; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Items; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.EnumAction; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SummonSkeleton extends Spell { + + public SummonSkeleton() { + super(Tier.APPRENTICE, 15, Element.NECROMANCY, "summon_skeleton", SpellType.MINION, 50, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + skeleton.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); + skeleton.setDropChance(EntityEquipmentSlot.MAINHAND, 0.0f); + world.spawnEntityInWorld(skeleton); + } + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 7.0f, 0.6f); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + skeleton.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); + skeleton.setDropChance(EntityEquipmentSlot.MAINHAND, 0.0f); + world.spawnEntityInWorld(skeleton); + } + caster.playSound(WizardrySounds.SPELL_SUMMONING, 7.0f, 0.6f); + return true; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonSkeletonLegion.java b/src/main/java/electroblob/wizardry/spell/SummonSkeletonLegion.java new file mode 100644 index 00000000..69a373ff --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonSkeletonLegion.java @@ -0,0 +1,75 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntitySkeletonMinion; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Items; +import net.minecraft.init.SoundEvents; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.EnumAction; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SummonSkeletonLegion extends Spell { + + public SummonSkeletonLegion() { + super(Tier.MASTER, 100, Element.NECROMANCY, "summon_skeleton_legion", SpellType.MINION, 400, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + // Archers + for(int i=0;i<3;i++){ + // This is all so much neater now thanks to this method. + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 3, 6); + // The spell instantly fails if no space was found (see javadoc for the above method). + if(pos == null) return false; + + EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(1200*modifiers.get(WizardryItems.duration_upgrade))); + skeleton.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); + skeleton.setItemStackToSlot(EntityEquipmentSlot.HEAD, new ItemStack(Items.CHAINMAIL_HELMET)); + skeleton.setItemStackToSlot(EntityEquipmentSlot.CHEST, new ItemStack(Items.CHAINMAIL_CHESTPLATE)); + skeleton.setDropChance(EntityEquipmentSlot.MAINHAND, 0.0f); + skeleton.setDropChance(EntityEquipmentSlot.HEAD, 0.0f); + skeleton.setDropChance(EntityEquipmentSlot.CHEST, 0.0f); + world.spawnEntityInWorld(skeleton); + } + // Swordsmen + for(int i=0;i<3;i++){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 3, 6); + // The spell instantly fails if no space was found (see javadoc for the above method). + if(pos == null) return false; + + EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(1200*modifiers.get(WizardryItems.duration_upgrade))); + skeleton.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.IRON_SWORD)); + skeleton.setItemStackToSlot(EntityEquipmentSlot.HEAD, new ItemStack(Items.CHAINMAIL_HELMET)); + skeleton.setItemStackToSlot(EntityEquipmentSlot.CHEST, new ItemStack(Items.CHAINMAIL_CHESTPLATE)); + skeleton.setDropChance(EntityEquipmentSlot.MAINHAND, 0.0f); + skeleton.setDropChance(EntityEquipmentSlot.HEAD, 0.0f); + skeleton.setDropChance(EntityEquipmentSlot.CHEST, 0.0f); + world.spawnEntityInWorld(skeleton); + } + } + + // Can't possibly get this far if nothing was spawned. + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonSnowGolem.java b/src/main/java/electroblob/wizardry/spell/SummonSnowGolem.java new file mode 100644 index 00000000..7867c74a --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonSnowGolem.java @@ -0,0 +1,48 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.monster.EntitySnowman; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SummonSnowGolem extends Spell { + + public SummonSnowGolem() { + super(Tier.APPRENTICE, 15, Element.ICE, "summon_snow_golem", SpellType.MINION, 20, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + if(!world.isRemote){ + EntitySnowman snowman = new EntitySnowman(world); + snowman.setPosition(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); + world.spawnEntityInWorld(snowman); + } + for(int i=0; i<10; i++){ + double x1 = (double)((float)pos.getX() + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)pos.getY() + 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)pos.getZ() + world.rand.nextFloat()*2 - 1.0F); + if(world.isRemote){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); + } + } + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonSpiritHorse.java b/src/main/java/electroblob/wizardry/spell/SummonSpiritHorse.java new file mode 100644 index 00000000..277bc15c --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonSpiritHorse.java @@ -0,0 +1,53 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntitySpiritHorse; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SummonSpiritHorse extends Spell { + + public SummonSpiritHorse() { + super(Tier.ADVANCED, 50, Element.EARTH, "summon_spirit_horse", SpellType.MINION, 150, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + WizardData properties = WizardData.get(caster); + + if(!properties.hasSpiritHorse){ + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntitySpiritHorse horse = new EntitySpiritHorse(world); + horse.setPosition(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); + horse.setTamedBy(caster); + horse.setHorseSaddled(true); + world.spawnEntityInWorld(horse); + } + properties.hasSpiritHorse = true; + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + return false; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonSpiritWolf.java b/src/main/java/electroblob/wizardry/spell/SummonSpiritWolf.java new file mode 100644 index 00000000..a7285484 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonSpiritWolf.java @@ -0,0 +1,52 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.WizardData; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntitySpiritWolf; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SummonSpiritWolf extends Spell { + + public SummonSpiritWolf() { + super(Tier.APPRENTICE, 25, Element.EARTH, "summon_spirit_wolf", SpellType.MINION, 100, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + WizardData properties = WizardData.get(caster); + + if(!properties.hasSpiritWolf){ + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntitySpiritWolf wolf = new EntitySpiritWolf(world); + wolf.setPosition(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); + wolf.setTamed(true); + wolf.setOwnerId(caster.getUniqueID()); + world.spawnEntityInWorld(wolf); + } + properties.hasSpiritWolf = true; + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + return false; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonStormElemental.java b/src/main/java/electroblob/wizardry/spell/SummonStormElemental.java new file mode 100644 index 00000000..2e61aa45 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonStormElemental.java @@ -0,0 +1,42 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntityStormElemental; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SummonStormElemental extends Spell { + + public SummonStormElemental() { + super(Tier.MASTER, 100, Element.LIGHTNING, "summon_storm_elemental", SpellType.MINION, 400, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntityStormElemental stormElemental = new EntityStormElemental(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(stormElemental); + } + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonWitherSkeleton.java b/src/main/java/electroblob/wizardry/spell/SummonWitherSkeleton.java new file mode 100644 index 00000000..d8b6c22a --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonWitherSkeleton.java @@ -0,0 +1,75 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntitySkeletonMinion; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.SkeletonType; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Items; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.EnumAction; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SummonWitherSkeleton extends Spell { + + public SummonWitherSkeleton() { + super(Tier.ADVANCED, 35, Element.NECROMANCY, "summon_wither_skeleton", SpellType.MINION, 150, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + skeleton.setSkeletonType(SkeletonType.WITHER); + skeleton.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.STONE_SWORD)); + skeleton.setDropChance(EntityEquipmentSlot.MAINHAND, 0.0f); + world.spawnEntityInWorld(skeleton); + } + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 7.0f, 0.6f); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + skeleton.setSkeletonType(SkeletonType.WITHER); + skeleton.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.STONE_SWORD)); + skeleton.setDropChance(EntityEquipmentSlot.MAINHAND, 0.0f); + world.spawnEntityInWorld(skeleton); + } + caster.playSound(WizardrySounds.SPELL_SUMMONING, 7.0f, 0.6f); + return true; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/SummonZombie.java b/src/main/java/electroblob/wizardry/spell/SummonZombie.java new file mode 100644 index 00000000..21718a87 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/SummonZombie.java @@ -0,0 +1,64 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.EntityZombieMinion; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class SummonZombie extends Spell { + + public SummonZombie() { + super(Tier.BASIC, 10, Element.NECROMANCY, "summon_zombie", SpellType.MINION, 40, EnumAction.BOW, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + EntityZombieMinion zombie = new EntityZombieMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(zombie); + } + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 7.0f, 0.6f); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(!world.isRemote){ + + BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); + if(pos == null) return false; + + EntityZombieMinion zombie = new EntityZombieMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.spawnEntityInWorld(zombie); + } + caster.playSound(WizardrySounds.SPELL_SUMMONING, 7.0f, 0.6f); + return true; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Telekinesis.java b/src/main/java/electroblob/wizardry/spell/Telekinesis.java new file mode 100644 index 00000000..d46608ea --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Telekinesis.java @@ -0,0 +1,114 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class Telekinesis extends Spell { + + public Telekinesis() { + super(Tier.BASIC, 5, Element.SORCERY, "telekinesis", SpellType.UTILITY, 5, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*modifiers.get(WizardryItems.range_upgrade), 3.0f); + + if(rayTrace != null && rayTrace.entityHit != null){ + + if(rayTrace.entityHit instanceof EntityItem){ + + Entity entityHit = rayTrace.entityHit; + entityHit.motionX = (caster.posX - entityHit.posX)/6; + entityHit.motionY = (caster.posY + caster.eyeHeight - entityHit.posY)/6; + entityHit.motionZ = (caster.posZ - entityHit.posZ)/6; + entityHit.playSound(WizardrySounds.SPELL_CONJURATION, 1.0F, 1.0f); + caster.swingArm(hand); + return true; + + }else if(rayTrace.entityHit instanceof EntityPlayer && Wizardry.settings.telekineticDisarmament){ + + EntityPlayer target = (EntityPlayer)rayTrace.entityHit; + + if(target.getHeldItemMainhand() != null){ + + if(!world.isRemote){ + EntityItem item = target.entityDropItem(target.getHeldItemMainhand(), 0.0f); + // Makes the item move towards the caster + item.motionX = (caster.posX - target.posX)/20; + item.motionZ = (caster.posZ - target.posZ)/20; + } + + target.setHeldItem(EnumHand.MAIN_HAND, null); + + target.playSound(WizardrySounds.SPELL_CONJURATION, 1.0F, 1.0f); + caster.swingArm(hand); + return true; + } + } + } + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + + IBlockState blockstate = world.getBlockState(new BlockPos(rayTrace.getBlockPos())); + + if(blockstate.getBlock().onBlockActivated(world, rayTrace.getBlockPos(), blockstate, caster, hand, null, rayTrace.sideHit, 0, 0, 0)){ + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0F, 1.0f); + caster.swingArm(hand); + return true; + } + } + return false; + } + + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target instanceof EntityPlayer && target.getHeldItemMainhand() != null){ + + // IDEA: Disarm the offhand if the mainhand is empty or otherwise harmless? + + if(!world.isRemote){ + EntityItem item = target.entityDropItem(target.getHeldItemMainhand(), 0.0f); + // Makes the item move towards the caster + item.motionX = (caster.posX - target.posX)/20; + item.motionZ = (caster.posZ - target.posZ)/20; + } + + target.setHeldItem(EnumHand.MAIN_HAND, null); + + target.playSound(WizardrySounds.SPELL_CONJURATION, 1.0F, 1.0f); + caster.swingArm(hand); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Thunderbolt.java b/src/main/java/electroblob/wizardry/spell/Thunderbolt.java new file mode 100644 index 00000000..a146975f --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Thunderbolt.java @@ -0,0 +1,65 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.projectile.EntityThunderbolt; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class Thunderbolt extends Spell { + + public Thunderbolt() { + super(Tier.BASIC, 10, Element.LIGHTNING, "thunderbolt", SpellType.ATTACK, 15, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(!world.isRemote){ + EntityThunderbolt thunderbolt = new EntityThunderbolt(world, caster, modifiers.get(SpellModifiers.DAMAGE)); + world.spawnEntityInWorld(thunderbolt); + } + + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 0.8F, world.rand.nextFloat() * 0.2F + 0.8F); + caster.swingArm(hand); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + EntityThunderbolt thunderbolt = new EntityThunderbolt(world, caster, modifiers.get(SpellModifiers.DAMAGE)); + thunderbolt.directTowards(target, 2.5f); + world.spawnEntityInWorld(thunderbolt); + } + + caster.playSound(WizardrySounds.SPELL_ICE, 0.8F, world.rand.nextFloat() * 0.2F + 0.8F); + caster.swingArm(hand); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Thunderstorm.java b/src/main/java/electroblob/wizardry/spell/Thunderstorm.java new file mode 100644 index 00000000..b1992941 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Thunderstorm.java @@ -0,0 +1,119 @@ +package electroblob.wizardry.spell; + +import java.util.List; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.EntityArc; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.effect.EntityLightningBolt; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class Thunderstorm extends Spell { + + public Thunderstorm() { + super(Tier.MASTER, 100, Element.LIGHTNING, "thunderstorm", SpellType.ATTACK, 250, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + if(world.canBlockSeeSky(new BlockPos(caster))){ + + for(int r=0; r<10; r++){ + + double radius = 4 + world.rand.nextDouble()*6*modifiers.get(WizardryItems.blast_upgrade); + double angle = world.rand.nextDouble()*Math.PI*2; + + double x = caster.posX + radius*Math.cos(angle); + double z = caster.posZ + radius*Math.sin(angle); + double y = WizardryUtilities.getNearestFloorLevel(world, new BlockPos(x, caster.posY, z), 10); + + if(!world.isRemote){ + EntityLightningBolt entitylightning = new EntityLightningBolt(world, x, y, z, false); + world.addWeatherEffect(entitylightning); + } + + // Code for eventhandler recognition; for achievements and such like. Left in for future use. + //NBTTagCompound entityNBT = entitylightning.getEntityData(); + //entityNBT.setInteger("summoningPlayer", entityplayer.entityId); + + // Secondary chaining effect + double seekerRange = 10.0d; + + List secondaryTargets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, x, y+1, z, world); + + // For this spell there is no limit to the amount of secondary targets! + for(EntityLivingBase secondaryTarget : secondaryTargets){ + + if(WizardryUtilities.isValidTarget(caster, secondaryTarget)){ + + if(!world.isRemote){ + EntityArc arc = new EntityArc(world); + arc.setEndpointCoords(x, y+1, z, + secondaryTarget.posX, secondaryTarget.posY + secondaryTarget.height/2, secondaryTarget.posZ); + world.spawnEntityInWorld(arc); + }else{ + for(int j=0;j<8;j++){ + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, secondaryTarget.posX + world.rand.nextFloat() - 0.5, secondaryTarget.getEntityBoundingBox().minY + secondaryTarget.height/2 + world.rand.nextFloat()*2 - 1, secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, secondaryTarget.posX + world.rand.nextFloat() - 0.5, secondaryTarget.getEntityBoundingBox().minY + secondaryTarget.height/2 + world.rand.nextFloat()*2 - 1, secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); + } + } + + secondaryTarget.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); + + secondaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 10.0f * modifiers.get(SpellModifiers.DAMAGE)); + + //Tertiary chaining effect + + List tertiaryTargets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, secondaryTarget.posX, secondaryTarget.posY + secondaryTarget.height/2, secondaryTarget.posZ, world); + + for(int j=0; j 2 && world.getBlockState(pos).getBlock() != WizardryBlocks.ice_statue){ + + pos = pos.offset(rayTrace.sideHit); + + if(WizardryUtilities.canBlockBeReplaced(world, pos)){ + world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); + } + + // Builds a 2 block high wall if it hits the ground + if(rayTrace.sideHit == EnumFacing.UP){ + pos = pos.offset(rayTrace.sideHit); + + if(WizardryUtilities.canBlockBeReplaced(world, pos)){ + world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); + } + } + } + } + + for(int i=0; i<20; i++){ + + if(world.isRemote){ + + double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, look.xCoord*modifiers.get(WizardryItems.range_upgrade), look.yCoord*modifiers.get(WizardryItems.range_upgrade), look.zCoord*modifiers.get(WizardryItems.range_upgrade), 8 + world.rand.nextInt(12), 0.4f, 0.6f, 1.0f); + + x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, look.xCoord*modifiers.get(WizardryItems.range_upgrade), look.yCoord*modifiers.get(WizardryItems.range_upgrade), look.zCoord*modifiers.get(WizardryItems.range_upgrade), 8 + world.rand.nextInt(12), 1.0f, 1.0f, 1.0f); + } + } + + if(ticksInUse % 12 == 0){ + if(ticksInUse == 0) WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 0.5F, 1.0f); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_ICE, 0.5F, 1.0f); + } + + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/WaterBreathing.java b/src/main/java/electroblob/wizardry/spell/WaterBreathing.java new file mode 100644 index 00000000..c132a85b --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/WaterBreathing.java @@ -0,0 +1,42 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +public class WaterBreathing extends Spell { + + public WaterBreathing() { + super(Tier.ADVANCED, 30, Element.EARTH, "water_breathing", SpellType.UTILITY, 250, EnumAction.BOW, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + caster.addPotionEffect(new PotionEffect(MobEffects.WATER_BREATHING, (int)(1200*modifiers.get(WizardryItems.duration_upgrade)), 0, false, false)); + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.3f, 0.3f, 1.0f); + } + } + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + return true; + } + + +} diff --git a/src/main/java/electroblob/wizardry/spell/Whirlwind.java b/src/main/java/electroblob/wizardry/spell/Whirlwind.java new file mode 100644 index 00000000..9a76561c --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Whirlwind.java @@ -0,0 +1,99 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.EnumAction; +import net.minecraft.network.play.server.SPacketEntityVelocity; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class Whirlwind extends Spell { + + public Whirlwind() { + super(Tier.APPRENTICE, 10, Element.EARTH, "whirlwind", SpellType.DEFENCE, 15, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.entityHit instanceof EntityLivingBase){ + EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; + + if(!world.isRemote){ + + target.motionX = caster.getLookVec().xCoord * 2; + target.motionY = caster.getLookVec().yCoord * 2 + 1; + target.motionZ = caster.getLookVec().zCoord * 2; + + // Player motion is handled on that player's client so needs packets + if(target instanceof EntityPlayerMP){ + ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + } + } + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x2 = (double)(caster.posX + world.rand.nextFloat() - 0.5F + caster.getLookVec().xCoord*caster.getDistanceToEntity(target)*0.5); + double y2 = (double)(WizardryUtilities.getPlayerEyesPos(caster) + world.rand.nextFloat() - 0.5F + caster.getLookVec().yCoord*caster.getDistanceToEntity(target)*0.5); + double z2 = (double)(caster.posZ + world.rand.nextFloat() - 0.5F + caster.getLookVec().zCoord*caster.getDistanceToEntity(target)*0.5); + world.spawnParticle(EnumParticleTypes.CLOUD, x2, y2, z2, caster.getLookVec().xCoord, caster.getLookVec().yCoord, caster.getLookVec().zCoord); + //Minecraft.getMinecraft().effectRenderer.addEffect(new EntitySparkleFX(world, x2, y2, z2, entityplayer.getLookVec().xCoord, entityplayer.getLookVec().yCoord, entityplayer.getLookVec().zCoord, null, 1.0f, 1.0f, 0.8f, 10)); + } + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 0.8F, world.rand.nextFloat() * 0.2F + 0.6F); + return true; + } + return false; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + target.motionX = caster.getLookVec().xCoord * 2; + target.motionY = caster.getLookVec().yCoord * 2 + 1; + target.motionZ = caster.getLookVec().zCoord * 2; + + // Player motion is handled on that player's client so needs packets + if(target instanceof EntityPlayerMP){ + ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + } + } + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x2 = (double)(caster.posX + world.rand.nextFloat() - 0.5F + caster.getLookVec().xCoord*caster.getDistanceToEntity(target)*0.5); + double y2 = (double)(caster.posY + caster.getEyeHeight() + world.rand.nextFloat() - 0.5F + caster.getLookVec().yCoord*caster.getDistanceToEntity(target)*0.5); + double z2 = (double)(caster.posZ + world.rand.nextFloat() - 0.5F + caster.getLookVec().zCoord*caster.getDistanceToEntity(target)*0.5); + world.spawnParticle(EnumParticleTypes.CLOUD, x2, y2, z2, caster.getLookVec().xCoord, caster.getLookVec().yCoord, caster.getLookVec().zCoord); + } + } + caster.swingArm(hand); + caster.playSound(WizardrySounds.SPELL_ICE, 0.8F, world.rand.nextFloat() * 0.2F + 0.6F); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/Wither.java b/src/main/java/electroblob/wizardry/spell/Wither.java new file mode 100644 index 00000000..878cf0bb --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Wither.java @@ -0,0 +1,113 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; + +public class Wither extends Spell { + + public Wither() { + super(Tier.APPRENTICE, 10, Element.NECROMANCY, "wither", SpellType.ATTACK, 20, EnumAction.NONE, false); + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + Vec3d look = caster.getLookVec(); + + RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + + EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; + + // Has no effect on withers or wither skeletons. + if(MagicDamage.isEntityImmune(DamageType.WITHER, target)){ + if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + }else{ + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.WITHER), 1.0f * modifiers.get(SpellModifiers.DAMAGE)); + target.addPotionEffect(new PotionEffect(MobEffects.WITHER, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 1)); + } + } + if(world.isRemote){ + for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + // I figured it out! when on client side, entityplayer.posY is at the eyes, not the feet! + // This is a test for lining up the ray with the wand tip. Not sure if I like it or not. + /* + Vec3d origin = Wizardry.proxy.getWandTipPosition(caster); + double x1 = origin.xCoord + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = origin.yCoord + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = origin.zCoord + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + */ + double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; + //world.spawnParticle("mobSpell", x1, y1, z1, -1*look.xCoord, -1*look.yCoord, -1*look.zCoord); + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.1f, 0.0f, 0.05f); + } + } + caster.swingArm(hand); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_HURT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + // Has no effect on withers or wither skeletons. + if(!MagicDamage.isEntityImmune(DamageType.WITHER, target) && !world.isRemote){ + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.WITHER), 1.0f * modifiers.get(SpellModifiers.DAMAGE)); + target.addPotionEffect(new PotionEffect(MobEffects.WITHER, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 1)); + } + + if(world.isRemote){ + + double dx = (target.posX - caster.posX)/caster.getDistanceToEntity(target); + double dy = (target.posY - caster.posY)/caster.getDistanceToEntity(target); + double dz = (target.posZ - caster.posZ)/caster.getDistanceToEntity(target); + + for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + + double x1 = caster.posX + dx*i/2 + world.rand.nextFloat()/5 - 0.1f; + double y1 = caster.posY + caster.getEyeHeight() - 0.4f + dy*i/2 + world.rand.nextFloat()/5 - 0.1f; + double z1 = caster.posZ + dz*i/2 + world.rand.nextFloat()/5 - 0.1f; + + Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.1f, 0.0f, 0.05f); + } + } + caster.swingArm(hand); + caster.playSound(SoundEvents.ENTITY_WITHER_HURT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/spell/WitherSkull.java b/src/main/java/electroblob/wizardry/spell/WitherSkull.java new file mode 100644 index 00000000..ce0f642e --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/WitherSkull.java @@ -0,0 +1,85 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.SpellType; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.projectile.EntityWitherSkull; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.EnumAction; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class WitherSkull extends Spell { + + public WitherSkull() { + super(Tier.ADVANCED, 20, Element.NECROMANCY, "wither_skull", SpellType.ATTACK, 30, EnumAction.NONE, false); + } + + @Override + public boolean doesSpellRequirePacket(){ + return false; + } + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + + Vec3d look = caster.getLookVec(); + + if(!world.isRemote){ + EntityWitherSkull witherskull = new EntityWitherSkull(world, caster, 1, 1, 1); + witherskull.setPosition( + caster.posX + look.xCoord, + caster.posY + look.yCoord + 1.3, + caster.posZ + look.zCoord); + witherskull.accelerationX = look.xCoord * 0.1; + witherskull.accelerationY = look.yCoord * 0.1; + witherskull.accelerationZ = look.zCoord * 0.1; + world.spawnEntityInWorld(witherskull); + WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SHOOT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + } + caster.swingArm(hand); + return true; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + + if(target != null){ + + if(!world.isRemote){ + + EntityWitherSkull witherskull = new EntityWitherSkull(world, caster, 1, 1, 1); + + double dx = target.posX - caster.posX; + double dy = target.getEntityBoundingBox().minY + (double)(target.height / 2.0F) - (caster.posY + (double)(caster.height / 2.0F)); + double dz = target.posZ - caster.posZ; + + witherskull.accelerationX = dx/caster.getDistanceToEntity(target) * 0.1; + witherskull.accelerationY = dy/caster.getDistanceToEntity(target) * 0.1; + witherskull.accelerationZ = dz/caster.getDistanceToEntity(target) * 0.1; + + witherskull.setPosition(caster.posX, caster.posY + caster.getEyeHeight(), caster.posZ); + + world.spawnEntityInWorld(witherskull); + caster.playSound(SoundEvents.ENTITY_WITHER_SHOOT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + } + + caster.swingArm(hand); + return true; + } + + return false; + } + + @Override + public boolean canBeCastByNPCs(){ + return true; + } + +} diff --git a/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java new file mode 100644 index 00000000..fdfa4f89 --- /dev/null +++ b/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java @@ -0,0 +1,265 @@ +package electroblob.wizardry.tileentity; + +import java.util.HashSet; +import java.util.Set; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.ItemArcaneTome; +import electroblob.wizardry.item.ItemArmourUpgrade; +import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.item.ItemWizardArmour; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.util.WandHelper; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; + +public class ContainerArcaneWorkbench extends Container { + + public TileEntityArcaneWorkbench tileEntityArcaneWorkbench; + + public static final ResourceLocation EMPTY_SLOT_CRYSTAL = new ResourceLocation(Wizardry.MODID, "gui/empty_slot_crystal"); + public static final ResourceLocation EMPTY_SLOT_UPGRADE = new ResourceLocation(Wizardry.MODID, "gui/empty_slot_upgrade"); + + public static final int CRYSTAL_SLOT = 8; + public static final int WAND_SLOT = 9; + public static final int UPGRADE_SLOT = 10; + private static final int[][][] SPELL_BOOK_SLOT_COORDS = { + {{80, 22}, {121, 51}, {106, 98}, {54, 98}, {39, 51}, {-999, -999}, {-999, -999}, {-999, -999}}, + {{80, 22}, {117, 43}, {117, 85}, {80, 106}, {43, 85}, {43, 43}, {-999, -999}, {-999, -999}}, + {{80, 22}, {113, 38}, {121, 74}, {98, 102}, {62, 102}, {39, 74}, {47, 38}, {-999, -999}}, + {{80, 22}, {111, 33}, {122, 64}, {111, 95}, {80, 106}, {49, 95}, {38, 64}, {49, 33}} + }; + + public ContainerArcaneWorkbench(IInventory inventory, TileEntityArcaneWorkbench tileentity){ + + this.tileEntityArcaneWorkbench = tileentity; + + ItemStack wand = tileentity.getStackInSlot(WAND_SLOT); + + for(int i=0; i<8; i++){ + this.addSlotToContainer(new SlotItemList(tileentity, i, -999, -999, 1, WizardryItems.spell_book)); + } + + this.addSlotToContainer(new SlotItemList(tileentity, CRYSTAL_SLOT, 8, 88, 64, WizardryItems.magic_crystal)) + .setBackgroundName(EMPTY_SLOT_CRYSTAL.toString()); + + this.addSlotToContainer(new SlotWandArmour(tileentity, WAND_SLOT, 80, 64, this)); + + Set upgrades = new HashSet(WandHelper.getSpecialUpgrades()); + upgrades.add(WizardryItems.arcane_tome); + upgrades.add(WizardryItems.armour_upgrade); + + this.addSlotToContainer(new SlotItemList(tileentity, UPGRADE_SLOT, 8, 106, 1, upgrades.toArray(new Item[0]))) + .setBackgroundName(EMPTY_SLOT_UPGRADE.toString()); + + for(int x = 0; x < 9; x++){ + this.addSlotToContainer(new Slot(inventory, x, 8 + x * 18, 196)); + } + + for(int y = 0; y < 3; y++){ + for(int x = 0; x < 9; x++){ + this.addSlotToContainer(new Slot(inventory, 9 + x + y * 9, 8 + x * 18, 138 + y * 18)); + } + } + + this.onSlotChanged(WAND_SLOT, wand, null); + } + + @Override + public boolean canInteractWith(EntityPlayer par1EntityPlayer) + { + return this.tileEntityArcaneWorkbench.isUseableByPlayer(par1EntityPlayer); + } + + /** + * Called from the central wand/armour slot when its item is changed or removed. + */ + // I wrote this! + public void onSlotChanged(int slotNumber, ItemStack stack, EntityPlayer player){ + + if(slotNumber == WAND_SLOT){ + + if(stack == null || (!(stack.getItem() instanceof ItemWand) && stack.getItem() != WizardryItems.blank_scroll)){ + // If the stack has been removed + for(int i=0; i

    + * In the future, there is scope for an entirely standalone mod based on this idea. Perhaps 'Elemental Damage' could be + * a config-only mod which allows its users to define any number of specific damage types, then give any creature a + * resistance, immunity or vulnerability to each, as well as being able to specify the sources for each type, such as + * enchantments, potions, specific items, certain entities, even particular situations and string damagesource names + * from other mods. + * + * @see IndirectMagicDamage + * @see IElementalDamage + * @since Wizardry 1.1 + * @author Electroblob */ +public class MagicDamage extends EntityDamageSource implements IElementalDamage { + + /** The name of the damagesource for direct magic damage from the wizardry mod. */ + public static final String DIRECT_MAGIC_DAMAGE = "wizardryMagic"; + /** The name of the damagesource for indirect magic damage from the wizardry mod. */ + public static final String INDIRECT_MAGIC_DAMAGE = "indirectWizardryMagic"; + // Technically, I don't need to specify that the classes in this map must extend entity, but it's good practice to. + private static final Map, DamageType[]> immunityMapping = new HashMap, DamageType[]>(); + + private final DamageType type; + private final boolean isRetaliatory; + + /** A simple set of constants for the types of damage. The names are deliberately different to those in EnumElement + * to avoid confusion. All types are classified as magic damage in the vanilla system, so witches are resistant to them. */ + public enum DamageType { + /** Generic magic damage from the wizardry mod. Like vanilla magic damage, except it doesn't bypass armour. */ + MAGIC, + /** Fire damage from the wizardry mod. Counts as fire damage in the vanilla system, so is blocked by any mobs that + * are immune to fire and entities with the fire resistance effect. */ + FIRE, + /** Frost (ice) damage from the wizardry mod. Snow golems, ice wraiths and ice giants are immune. */ + FROST, + /** Shock (lightning) damage from the wizardry mod. Lightning wraiths and storm elementals are immune. */ + SHOCK, + /** Wither damage from the wizardry mod. Withers, wither skeletons and shadow wraiths are immune. */ + WITHER, + /** Poison damage from the wizardry mod. Spiders, cave spiders and undead mobs are immune. */ + POISON, + /** [NYI] Force damage from the wizardry mod. Insubstantial creatures (ghast, shadow wraith, etc.) are immune. */ + FORCE, + /** Blast damage from the wizardry mod. Affected by the blast protection enchantment. */ + BLAST, + /** [NYI] Radiant damage from the wizardry mod. */ + RADIANT; + } + + static { + // Of course, the entities that are immune to fire already are since there's a vanilla system for that, but + // they're included here anyway for completeness and in case anyone wants to check if an entity is immune to + // an unspecified element for reasons other than dealing damage. + setEntityImmunities(EntityPhoenix.class, DamageType.FIRE); + setEntityImmunities(EntityBlaze.class, DamageType.FIRE); + setEntityImmunities(EntityBlazeMinion.class, DamageType.FIRE); + setEntityImmunities(EntityPigZombie.class, DamageType.FIRE, DamageType.POISON); + setEntityImmunities(EntityMagmaCube.class, DamageType.FIRE); + setEntityImmunities(EntityGhast.class, DamageType.FIRE); + setEntityImmunities(EntityDragon.class, DamageType.FIRE); + setEntityImmunities(EntityStormElemental.class, DamageType.FIRE, DamageType.SHOCK); + setEntityImmunities(EntityWither.class, DamageType.FIRE, DamageType.WITHER); + setEntityImmunities(EntitySnowman.class, DamageType.FROST); + setEntityImmunities(EntityIceWraith.class, DamageType.FROST); + setEntityImmunities(EntityIceGiant.class, DamageType.FROST); + setEntityImmunities(EntityLightningWraith.class, DamageType.SHOCK); + setEntityImmunities(EntityShadowWraith.class, DamageType.WITHER); + setEntityImmunities(EntitySpider.class, DamageType.POISON); + setEntityImmunities(EntitySpiderMinion.class, DamageType.POISON); + setEntityImmunities(EntityCaveSpider.class, DamageType.POISON); + setEntityImmunities(EntityZombie.class, DamageType.POISON); + setEntityImmunities(EntitySkeleton.class, DamageType.POISON); + setEntityImmunities(EntityZombieMinion.class, DamageType.POISON); + setEntityImmunities(EntitySkeletonMinion.class, DamageType.POISON); + } + + public MagicDamage(String name, Entity caster, DamageType type, boolean isRetaliatory){ + super(name, caster); + this.type = type; + this.isRetaliatory = isRetaliatory; + this.setMagicDamage(); + if(type == DamageType.FIRE) this.setFireDamage(); + if(type == DamageType.BLAST) this.setExplosion(); + } + + /** Returns true if the given entity is immune to the given damage type according to the entity immunity mappings, + * false otherwise. When you want to check for resistances, check this method rather than just checking the result + * of attackEntityFrom, since that could return false for all sorts of reasons besides immunities. However, if you + * don't need to know whether the damage succeeded or not, there's no point in checking this method. A common use + * of this method is to check for immunity and if so display the "[mob] resisted [spell]" chat message. See + * {@link electroblob.wizardry.spell.Arc Arc} for a good example of this, and also of when not to use it. */ + public static boolean isEntityImmune(DamageType type, Entity entity){ + // Because Mojang, in their infinite wisdom, did not make wither skeletons their own separate class (despite the + // fact that cave spiders are), I have to test for this manually. Realistically, no mod author would be stupid + // enough to put two entities in one class, so this should be the only time I ever have to do this. + if(type == DamageType.WITHER && entity instanceof EntitySkeleton + // Don't need to check EntitySkeletonMinion separately any more because it now extends EntitySkeleton. + && ((EntitySkeleton)entity).getSkeletonType() == SkeletonType.WITHER){ + return true; + } + + if(type == DamageType.FIRE && entity.isImmuneToFire()) return true; + + DamageType[] immunities = MagicDamage.immunityMapping.get(entity.getClass()); + + return immunities != null && Arrays.asList(immunities).contains(type); + } + + /** Registers the given type of entity as immune to all of the passed in damage types. */ + public static void setEntityImmunities(Class entityType, DamageType... immunities){ + immunityMapping.put(entityType, immunities); + } + + /** Adds the passed in damage type to the list of damage types to which the given entity is immune. */ + public static void addEntityImmunity(Class entityType, DamageType immunity){ + List immunities = Arrays.asList(immunityMapping.get(entityType)); + immunities.add(immunity); + // Apparently putting 0 here works just fine. + immunityMapping.put(entityType, immunities.toArray(new DamageType[0])); + } + + /** + * Returns a DamageSource called "wizardryMagic" with the given entity as the caster. Use in preference to vanilla + * types to allow things to distinguish between magic and regular melee/swords. Unlike DamageSource.magic, it does + * not bypass armour and has a player as the source (rather than nothing). It is still classed as magic damage (for + * the record, all this does in vanilla is make witches 85% resistant to it - but that seems kinda right anyway). + * isRetaliatory defaults to false. + *

    + * Now that this is its own class, this static method is largely redundant, but it's not worth refactoring the entire + * mod just to get rid of this method and use the constructor instead. + * @param caster The player or other living entity causing the damage + * @param type The type that this damage belongs to; used for resistances and wand perks. Use + * {@link DamageType#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element + * even though the spell has one - for example, not all necromancy spells are 'withery', so some of them might + * reasonably affect creatures that are ususally immune to wither effects). + * @return A damagesource object of type EntityDamageSource + */ + public static DamageSource causeDirectMagicDamage(Entity caster, DamageType type){ + return causeDirectMagicDamage(caster, type, false); + } + + /** + * Returns a DamageSource called "wizardryMagic" with the given entity as the caster. Use in preference to vanilla + * types to allow things to distinguish between magic and regular melee/swords. Unlike DamageSource.magic, it does + * not bypass armour and has a player as the source (rather than nothing). It is still classed as magic damage (for + * the record, all this does in vanilla is make witches 85% resistant to it - but that seems kinda right anyway). + *

    + * Now that this is its own class, this static method is largely redundant, but it's not worth refactoring the entire + * mod just to get rid of this method and use the constructor instead. + * @param caster The player or other living entity causing the damage + * @param type The type that this damage belongs to; used for resistances and wand perks. Use + * {@link DamageType#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element + * even though the spell has one - for example, not all necromancy spells are 'withery', so some of them might + * reasonably affect creatures that are ususally immune to wither effects). + * @param isRetaliatory whether this damage source came from a retaliatory attack; prevents infinite loops + * occurring when two entities damage each other with retaliatory effects. + * @return A damagesource object of type EntityDamageSource + */ + public static DamageSource causeDirectMagicDamage(Entity caster, DamageType type, boolean isRetaliatory){ + return new MagicDamage(DIRECT_MAGIC_DAMAGE, caster, type, isRetaliatory); + } + + /** + * Returns a DamageSource called "indirectWizardryMagic" with the player as the caster. Use in preference to vanilla + * types to allow things to distinguish between magic and regular arrows/throwables. Unlike + * DamageSource.causeIndirectMagicDamage, it does not bypass armour. It is still classed as magic damage (for the + * record, all this does in vanilla is make witches 85% resistant to it - but that seems kinda right anyway). + * isRetaliatory defaults to false. + *

    + * Now that this is its own class, this static method is largely redundant, but it's not worth refactoring the entire + * mod just to get rid of this method and use the constructor instead. + * @param magic The entity that actually caused the damage + * @param caster The player or other living entity that cast the spell originally + * @param type The type that this damage belongs to; used for resistances and wand perks. Use + * {@link DamageType#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element + * even though the spell has one - for example, not all necromancy spells are 'withery', so some of them might + * reasonably affect creatures that are ususally immune to wither effects). + * @return A damagesource object of type EntityDamageSourceIndirect + */ + public static DamageSource causeIndirectMagicDamage(Entity magic, Entity caster, DamageType type){ + return causeIndirectMagicDamage(magic, caster, type, false); + } + + /** + * Returns a DamageSource called "indirectWizardryMagic" with the player as the caster. Use in preference to vanilla + * types to allow things to distinguish between magic and regular arrows/throwables. Unlike + * DamageSource.causeIndirectMagicDamage, it does not bypass armour. It is still classed as magic damage (for the + * record, all this does in vanilla is make witches 85% resistant to it - but that seems kinda right anyway). + *

    + * Now that this is its own class, this static method is largely redundant, but it's not worth refactoring the entire + * mod just to get rid of this method and use the constructor instead. + * @param magic The entity that actually caused the damage + * @param caster The player or other living entity that cast the spell originally + * @param type The type that this damage belongs to; used for resistances and wand perks. Use + * {@link DamageType#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element + * even though the spell has one - for example, not all necromancy spells are 'withery', so some of them might + * reasonably affect creatures that are ususally immune to wither effects). + * @param isRetaliatory whether this damage source came from a retaliatory attack; prevents infinite loops + * occurring when two entities damage each other with retaliatory effects. + * @return A damagesource object of type EntityDamageSourceIndirect + */ + public static DamageSource causeIndirectMagicDamage(Entity magic, Entity caster, DamageType type, boolean isRetaliatory){ + return new IndirectMagicDamage(INDIRECT_MAGIC_DAMAGE, magic, caster, type, isRetaliatory); + } + + @Override + public DamageType getType(){ + return type; + } + + @Override + public boolean isRetaliatory(){ + return isRetaliatory; + } + +} diff --git a/src/main/java/electroblob/wizardry/util/MinionDamage.java b/src/main/java/electroblob/wizardry/util/MinionDamage.java new file mode 100644 index 00000000..987baf88 --- /dev/null +++ b/src/main/java/electroblob/wizardry/util/MinionDamage.java @@ -0,0 +1,32 @@ +package electroblob.wizardry.util; + +import electroblob.wizardry.util.MagicDamage.DamageType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; + +/** + * DamageSource specifically for summoned creatures. Despite being for melee attacks, this is actually an indirect + * damage source, because it allows the minion to be the 'projectile'. This class also overrides getDeathMessage to + * display the minion's name rather than the caster's. The event handler deals with all damage dealt by summoned + * creatures, so it's very unlikely that this will be needed anywhere else. If for some reason it is, note that + * knockback has to be removed and re-applied after the damage is dealt. + * @author Electroblob + * @since Wizardry 1.2 + * @see IndirectMinionDamage + */ +public class MinionDamage extends IndirectMagicDamage { + + public MinionDamage(String name, Entity minion, Entity caster, DamageType type, boolean isRetaliatory) { + super(name, minion, caster, type, isRetaliatory); + } + + @Override + public ITextComponent getDeathMessage(EntityLivingBase victim) { + ITextComponent itextcomponent = this.damageSourceEntity.getDisplayName(); + String key = "death.attack." + this.damageType; + return new TextComponentTranslation(key, victim.getDisplayName(), itextcomponent); + } + +} diff --git a/src/main/java/electroblob/wizardry/util/SpellModifiers.java b/src/main/java/electroblob/wizardry/util/SpellModifiers.java new file mode 100644 index 00000000..39f97c63 --- /dev/null +++ b/src/main/java/electroblob/wizardry/util/SpellModifiers.java @@ -0,0 +1,141 @@ +package electroblob.wizardry.util; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import io.netty.buffer.ByteBuf; +import net.minecraft.item.Item; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fml.common.network.ByteBufUtils; + +/** + * Object that wraps any number of spell multipliers into one, allowing for expandability within the Spell#cast + * methods. This class is essentially a glorified {@link Map} which can be written to and read from a {@link ByteBuf}. + * It is possible to calculate spell multipliers from wand NBT within the cast methods, but this is cumbersome and does + * not allow the multipliers to be sent to the client, which is sometimes necessary (for example, detonate needs to know + * about range multipliers on the client side or the particles wouldn't show outside of the base range). + *

    + * Most external interaction with SpellModifiers objects will be in SpellCastEvent.Pre, where you can add additional + * multipliers to them if desired for use with your own spells, or modify the existing ones. If you have added a wand + * upgrade, this is not done automatically for you; you will have to do it yourself (for the simple reason that + * not all wand upgrades affect spells). + *

    + * To use a SpellModifiers object within the Spell.cast methods, simply retrieve the desired multiplier + * using {@link SpellModifiers#get(Item)} for wand upgrades, or {@link SpellModifiers#get(String)} if the multiplier + * is not from a wand upgrade. + * @author Electroblob + * @since Wizardry 1.2 + * @see WandHelper + */ +// I have made the decision that the USERS of this class must decide whether the multipliers need syncing or not, on a +// case-by-case basis. Why? Because assigning keys to either sync or not sync would be unnecessarily restrictive, and +// would mean they have to be registered, and part of the point of SpellModifiers is that they can be added to on the +// fly. +public final class SpellModifiers { + + /** Constant string identifier for the damage multiplier. All the other multipliers in Wizardry have items. */ + public static final String DAMAGE = "damage"; + + private Map multiplierMap; + private Map syncedMultiplierMap; + + /** Creates an empty SpellModifiers object. All calls to get(...) on an empty SpellModifiers object + * will return a value of 1. */ + public SpellModifiers(){ + multiplierMap = new HashMap(); + syncedMultiplierMap = new HashMap(); + } + + /** + * Adds the given multiplier to this SpellModifiers object, using the string identifier that the given wand + * upgrade item was registered with. + * @throws IllegalArgumentException if the given item is not a registered special wand upgrade. + * @param upgrade The upgrade item the multiplier corresponds to. + * @param multiplier The multiplier value, with 1 being default. Usage of multipliers is up to individual spells to + * implement. + * @param needsSyncing Whether this multiplier should be synchronised with the client via packets. Only set this + * to true if particles will be spawned which need to know the value of the multiplier. + * @return The SpellModifiers object, allowing this method to be chained onto the constructor. + */ + public SpellModifiers set(Item upgrade, float multiplier, boolean needsSyncing){ + this.set(WandHelper.getIdentifier(upgrade), multiplier, needsSyncing); + return this; + } + + /** + * Adds the given multiplier to this SpellModifiers object, using the given string key. In most cases, the + * multiplier will correspond to a wand upgrade, in which case use {@link SpellModifiers#set(Item, float, boolean)} + * instead. + * @param key The key used to identify the multiplier. + * @param multiplier The multiplier value, with 1 being default. Usage of multipliers is up to individual spells to + * implement. + * @param needsSyncing Whether this multiplier should be synchronised with the client via packets. Only set this + * to true if particles will be spawned which depend on the multiplier. + * @return The SpellModifiers object, allowing this method to be chained onto the constructor. + */ + public SpellModifiers set(String key, float multiplier, boolean needsSyncing){ + multiplierMap.put(key, multiplier); + if(needsSyncing) syncedMultiplierMap.put(key, multiplier); + return this; + } + + /** Returns the multiplier corresponding to the given wand upgrade item, or 1 if no multiplier was stored. + * @throws IllegalArgumentException if the given item is not a registered special wand upgrade. */ + public float get(Item upgrade){ + return get(WandHelper.getIdentifier(upgrade)); + } + + /** Returns the multiplier corresponding to the given string key, or 1 if no multiplier was stored. In most cases, + * the multiplier will correspond to a wand upgrade, in which case use {@link SpellModifiers#get(Item)} + * instead. */ + public float get(String key){ + Float value = multiplierMap.get(key); + // Must check for null before unboxing, and if it is null, return the default 1. + return value == null ? 1 : value; + } + + /** Returns an unmodifiable map of the modifiers stored in this SpellModifiers object. Useful for iterating through + * the modifiers. */ + public Map getModifiers(){ + return Collections.unmodifiableMap(this.multiplierMap); + } + + /** Removes all modifiers from this SpellModifiers object, effectively resetting them all to 1. */ + public void reset(){ + this.multiplierMap.clear(); + this.syncedMultiplierMap.clear(); + } + + /** Reads this SpellModifiers object from the given ByteBuf. */ + public void read(ByteBuf buf){ + int entryCount = buf.readInt(); + for(int i=0; i entry : syncedMultiplierMap.entrySet()){ + ByteBufUtils.writeUTF8String(buf, entry.getKey()); + buf.writeFloat(entry.getValue()); + } + } + + /** Creates a new SpellModifiers object from the given NBTTagCompound. The NBTTagCompound should have 1 or more + * float tags, which will be stored as modifiers under the same name as the tag. For example, the following NBT + * tag (in command syntax) will create a SpellModifiers object with a damage modifier of 1.5 and a range modifier of + * 2:

    {damage:1.5, range:2}

    + * Note that needsSyncing is set to true for all returned modifiers. */ + public static SpellModifiers fromNBT(NBTTagCompound nbt){ + SpellModifiers modifiers = new SpellModifiers(); + for(String key : nbt.getKeySet()){ + modifiers.set(key, nbt.getFloat(key), true); + } + return modifiers; + } + +} diff --git a/src/main/java/electroblob/wizardry/util/WandHelper.java b/src/main/java/electroblob/wizardry/util/WandHelper.java new file mode 100644 index 00000000..436f220f --- /dev/null +++ b/src/main/java/electroblob/wizardry/util/WandHelper.java @@ -0,0 +1,290 @@ +package electroblob.wizardry.util; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Set; + +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.spell.Spell; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +/** Much like {@link net.minecraft.enchantment.EnchantmentHelper EnchantmentHelper}, this class has some static methods + * which allow cleaner and more concise interaction with the wand NBT data, which is quite a complex structure. Such + * interaction previously resulted in rather verbose and repetitive code which was hard to read and even harder to debug! + * For example, this class allowed {@link electroblob.wizardry.item.ItemWand ItemWand} to be shortened by about 80 lines. + * In addition, by having all the various null checks and array size checks in one place, the chance of accidental errors + * due to forgetting to check these things is greatly reduced. + *

    + * Note that these methods contain no game logic at all; they are purely for interacting with the NBT data. Conversely, + * you should never need to access the wand's NBT data directly when using this class, but the keys are public in the + * unlikely case that this is necessary. + *

    + * Also note that none of the methods in this class actually check that the given ItemStack contains an ItemWand; you + * can, for example, pass in a stack of snowballs without causing problems, but that is of course pointless! + *

    + * All get methods in this class return some kind of default if the passed-in wand stack has no nbt data. + * See individual method descriptions for more details.
    + * All set methods in this class create a new nbt data for the passed-in wand if it has none, before doing + * whatever else they do. + * @see electroblob.wizardry.item.ItemWand ItemWand + * @see electroblob.wizardry.packet.PacketControlInput PacketControlInput + * @since Wizardry 1.1 */ +public final class WandHelper { + + // NBT tag keys + public static final String SPELL_ARRAY_KEY = "spells"; + public static final String SELECTED_SPELL_KEY = "selectedSpell"; + public static final String COOLDOWN_ARRAY_KEY = "cooldown"; + public static final String UPGRADES_KEY = "upgrades"; + + private static final HashMap upgradeMap = new HashMap(); + + static { + upgradeMap.put(WizardryItems.condenser_upgrade, "condenser"); + upgradeMap.put(WizardryItems.storage_upgrade, "storage"); + upgradeMap.put(WizardryItems.siphon_upgrade, "siphon"); + upgradeMap.put(WizardryItems.range_upgrade, "range"); + upgradeMap.put(WizardryItems.duration_upgrade, "duration"); + upgradeMap.put(WizardryItems.cooldown_upgrade, "cooldown"); + upgradeMap.put(WizardryItems.blast_upgrade, "blast"); + upgradeMap.put(WizardryItems.attunement_upgrade, "attunement"); + } + + /** Returns an array containing the spells currently bound to the given wand. As of Wizardry 1.1, this array is not + * always the same size; it can be anywhere between 5 and 8 (inclusive) in length. If the wand has no spell data, + * returns an array of length 0. */ + public static Spell[] getSpells(ItemStack wand){ + + Spell[] spells = new Spell[0]; + + if(wand.getTagCompound() != null){ + + int[] spellIDs = wand.getTagCompound().getIntArray(SPELL_ARRAY_KEY); + + spells = new Spell[spellIDs.length]; + + for(int i=0; i= numberOfSpells - 1){ + selectedSpell = 0; + }else{ + selectedSpell++; + } + + wand.getTagCompound().setInteger(SELECTED_SPELL_KEY, selectedSpell); + + } + } + + /** Selects the previous spell in this wand's list of spells. */ + public static void selectPreviousSpell(ItemStack wand){ + + // 5 here because if the spell array doesn't exist, the wand can't possibly have attunement upgrades + if(getSpells(wand).length < 0) setSpells(wand, new Spell[5]); + // This cannot possibly be null here, and yet I am getting an NPE... + if(wand.getTagCompound() != null){ + + int numberOfSpells = getSpells(wand).length; + int selectedSpell = wand.getTagCompound().getInteger(SELECTED_SPELL_KEY); + + if(selectedSpell <= 0){ + selectedSpell = numberOfSpells - 1; + }else{ + selectedSpell--; + } + + wand.getTagCompound().setInteger(SELECTED_SPELL_KEY, selectedSpell); + } + } + + /** Returns an array of the cooldowns for each spell bound to the given wand. As of Wizardry 1.1, this array is not + * always the same size; it can be anywhere between 5 and 8 (inclusive) in length. If the wand has no cooldown data, + * returns an array of length 0. */ + public static int[] getCooldowns(ItemStack wand){ + + int[] cooldowns = new int[0]; + + if(wand.getTagCompound() != null){ + + return wand.getTagCompound().getIntArray(COOLDOWN_ARRAY_KEY); + } + + return cooldowns; + } + + /** Sets the given wand's cooldown array. The array can be anywhere between 5 and 8 (inclusive) in length. */ + public static void setCooldowns(ItemStack wand, int[] cooldowns){ + + if(wand.getTagCompound() == null) wand.setTagCompound((new NBTTagCompound())); + + wand.getTagCompound().setIntArray(COOLDOWN_ARRAY_KEY, cooldowns); + } + + /** Decrements the cooldown for each spell bound to the given wand by 1, if that cooldown is greater than 0. */ + public static void decrementCooldowns(ItemStack wand){ + + int[] cooldowns = getCooldowns(wand); + + // If there are no cooldowns, it is assumed that they are all zero and therefore nothing needs to be done. + if(cooldowns.length == 0) return; + + for(int i=0; i 0) cooldowns[i]--; + } + + setCooldowns(wand, cooldowns); + } + + /** Returns the given wand's cooldown for the currently selected spell, or 0 if the wand has no cooldown data. */ + public static int getCurrentCooldown(ItemStack wand){ + + int[] cooldowns = getCooldowns(wand); + + if(cooldowns.length == 0) return 0; + // Don't need to check if the tag compound is null since the above check is equivalent. + return cooldowns[wand.getTagCompound().getInteger(SELECTED_SPELL_KEY)]; + } + + /** Sets the given wand's cooldown for the currently selected spell. */ + public static void setCurrentCooldown(ItemStack wand, int cooldown){ + + if(wand.getTagCompound() == null) wand.setTagCompound((new NBTTagCompound())); + + int[] cooldowns = getCooldowns(wand); + + // The length of the spells array must be greater than 0 since this method can only be called if a spell is + // cast, which is impossible if there are no spells. + if(cooldowns.length == 0) cooldowns = new int[getSpells(wand).length]; + + cooldowns[wand.getTagCompound().getInteger(SELECTED_SPELL_KEY)] = cooldown; + + setCooldowns(wand, cooldowns); + } + + /** Returns the number of upgrades of the given type that have been applied to the given wand, or 0 if the wand has + * no upgrade data or the given item is not a valid wand upgrade. */ + public static int getUpgradeLevel(ItemStack wand, Item upgrade){ + + String key = upgradeMap.get(upgrade); + + if(wand.getTagCompound() != null && wand.getTagCompound().hasKey(UPGRADES_KEY) && key != null){ + return wand.getTagCompound().getCompoundTag(UPGRADES_KEY).getInteger(key); + } + + return 0; + + } + + /** Returns the total number of upgrades that have been applied to the given wand, or 0 if the wand has no upgrade + * data. */ + public static int getTotalUpgrades(ItemStack wand){ + + int totalUpgrades = 0; + + for(Item item : upgradeMap.keySet()){ + totalUpgrades += getUpgradeLevel(wand, item); + } + + return totalUpgrades; + } + + /** Applies the given upgrade to the given wand, or in other words increases the level for that upgrade by 1. This + * does not account for the individual or total upgrade stack limits. */ + public static void applyUpgrade(ItemStack wand, Item upgrade){ + + if(wand.getTagCompound() == null) wand.setTagCompound((new NBTTagCompound())); + + if(!wand.getTagCompound().hasKey(UPGRADES_KEY)) wand.getTagCompound().setTag(UPGRADES_KEY, new NBTTagCompound()); + + NBTTagCompound upgrades = wand.getTagCompound().getCompoundTag(UPGRADES_KEY); + + String key = upgradeMap.get(upgrade); + + if(key != null) upgrades.setInteger(key, upgrades.getInteger(key) + 1); + + wand.getTagCompound().setTag(UPGRADES_KEY, upgrades); + } + + /** Returns true if the given item is a valid special wand upgrade. */ + public static boolean isWandUpgrade(Item upgrade){ + return upgradeMap.containsKey(upgrade); + } + + /** Returns an unmodifiable set of all the items which are valid special wand upgrades. */ + public static Set getSpecialUpgrades(){ + return Collections.unmodifiableSet(WandHelper.upgradeMap.keySet()); + } + + /** Package-protected getter for the identifier that corresponds to the given item, used only in the + * {@link SpellModifiers} class. Internal to Wizardry. + * @throws IllegalArgumentException if the given item is not a registered special wand upgrade.*/ + static String getIdentifier(Item upgrade){ + if(!isWandUpgrade(upgrade)) throw new IllegalArgumentException("Tried to get a wand upgrade key for an item" + + "that is not a registered special wand upgrade."); + return upgradeMap.get(upgrade); + } + + /** Registers a special upgrade with wizardry. Not used in the base mod, but I've put it here to make it easy + * for add-ons to add new wand upgrades. + * @param upgrade The wand upgrade item + * @param identifier A unique string, used as a key for wand nbt tags + * @throws IllegalArgumentException if the passed in identifier is already used for another wand upgrade */ + public static void registerSpecialUpgrade(Item upgrade, String identifier){ + // Throwing an exception is the best thing to do here, since if a duplicate was allowed weird things would + // happen later with wand NBT. + if(upgradeMap.containsValue(identifier)) throw new IllegalArgumentException("Duplicate wand upgrade identifier: " + identifier); + upgradeMap.put(upgrade, identifier); + } +} diff --git a/src/main/java/electroblob/wizardry/util/WizardryParticleType.java b/src/main/java/electroblob/wizardry/util/WizardryParticleType.java new file mode 100644 index 00000000..53093f2c --- /dev/null +++ b/src/main/java/electroblob/wizardry/util/WizardryParticleType.java @@ -0,0 +1,19 @@ +package electroblob.wizardry.util; + +/** Enum constants representing the different types of particle added by wizardry. This was renamed from the previous + * EnumParticleType in the 1.10.2 port to avoid confusion with the vanilla version, EnumParticleTypes. */ +public enum WizardryParticleType { + BLIZZARD, + BRIGHT_DUST, + DARK_MAGIC, + DUST, + ICE, + LEAF, + MAGIC_BUBBLE, + MAGIC_FIRE, + PATH, + SNOW, + SPARK, + SPARKLE, + SPARKLE_ROTATING +} diff --git a/src/main/java/electroblob/wizardry/util/WizardryPathFinder.java b/src/main/java/electroblob/wizardry/util/WizardryPathFinder.java new file mode 100644 index 00000000..8c5c2d9d --- /dev/null +++ b/src/main/java/electroblob/wizardry/util/WizardryPathFinder.java @@ -0,0 +1,136 @@ +package electroblob.wizardry.util; + +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.collect.Sets; + +import net.minecraft.entity.EntityLiving; +import net.minecraft.pathfinding.NodeProcessor; +import net.minecraft.pathfinding.Path; +import net.minecraft.pathfinding.PathHeap; +import net.minecraft.pathfinding.PathPoint; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; + +/** Minecraft's pathfinder refused to play nicely, so I 'borrowed' its code and fiddled with it. */ +public class WizardryPathFinder { + + /** The path being generated */ + private final PathHeap path = new PathHeap(); + private final Set closedSet = Sets.newHashSet(); + /** Selection of path points to add to the path */ + private final PathPoint[] pathOptions = new PathPoint[32]; + private final NodeProcessor nodeProcessor; + + public WizardryPathFinder(NodeProcessor processor){ + this.nodeProcessor = processor; + } + + @Nullable + public Path findPath(IBlockAccess world, EntityLiving entity, BlockPos destination, float range){ + return this.findPath(world, entity, (double)((float)destination.getX() + 0.5F), (double)((float)destination.getY() + 0.5F), (double)((float)destination.getZ() + 0.5F), range); + } + + @Nullable + private Path findPath(IBlockAccess world, EntityLiving entity, double x, double y, double z, float range){ + this.path.clearPath(); + this.nodeProcessor.initProcessor(world, entity); + PathPoint pathpoint = this.nodeProcessor.getStart(); + PathPoint pathpoint1 = this.nodeProcessor.getPathPointToCoords(x, y, z); + Path path = this.findPath(pathpoint, pathpoint1, range); + this.nodeProcessor.postProcess(); + return path; + } + + @Nullable + private Path findPath(PathPoint start, PathPoint end, float range){ + + start.totalPathDistance = 0.0F; + start.distanceToNext = start.distanceManhattan(end); + start.distanceToTarget = start.distanceToNext; + this.path.clearPath(); + this.closedSet.clear(); + this.path.addPoint(start); + PathPoint pathpoint = start; + int i = 0; + + while(!this.path.isPathEmpty()){ + + ++i; + + // This is the offending line - timeout limit changed from 200 to 5000. + if(i >= 5000){ + break; + } + + PathPoint pathpoint1 = this.path.dequeue(); + + if(pathpoint1.equals(end)){ + pathpoint = end; + break; + } + + if(pathpoint1.distanceManhattan(end) < pathpoint.distanceManhattan(end)){ + pathpoint = pathpoint1; + } + + pathpoint1.visited = true; + int j = this.nodeProcessor.findPathOptions(this.pathOptions, pathpoint1, end, range); + + for(int k = 0; k < j; ++k){ + + PathPoint pathpoint2 = this.pathOptions[k]; + float f = pathpoint1.distanceManhattan(pathpoint2); + pathpoint2.distanceFromOrigin = pathpoint1.distanceFromOrigin + f; + pathpoint2.cost = f + pathpoint2.costMalus; + float f1 = pathpoint1.totalPathDistance + pathpoint2.cost; + + if(pathpoint2.distanceFromOrigin < range && (!pathpoint2.isAssigned() || f1 < pathpoint2.totalPathDistance)){ + + pathpoint2.previous = pathpoint1; + pathpoint2.totalPathDistance = f1; + pathpoint2.distanceToNext = pathpoint2.distanceManhattan(end) + pathpoint2.costMalus; + + if(pathpoint2.isAssigned()){ + this.path.changeDistance(pathpoint2, pathpoint2.totalPathDistance + pathpoint2.distanceToNext); + }else{ + pathpoint2.distanceToTarget = pathpoint2.totalPathDistance + pathpoint2.distanceToNext; + this.path.addPoint(pathpoint2); + } + } + } + } + + if(pathpoint == start){ + return null; + }else{ + Path path = this.createEntityPath(start, pathpoint); + return path; + } + } + + /** + * Returns a new PathEntity for a given start and end point + */ + private Path createEntityPath(PathPoint start, PathPoint end){ + + int i = 1; + + for(PathPoint pathpoint = end; pathpoint.previous != null; pathpoint = pathpoint.previous){ + ++i; + } + + PathPoint[] points = new PathPoint[i]; + PathPoint pathpoint1 = end; + --i; + + for(points[i] = end; pathpoint1.previous != null; points[i] = pathpoint1){ + pathpoint1 = pathpoint1.previous; + --i; + } + + return new Path(points); + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/util/WizardryUtilities.java b/src/main/java/electroblob/wizardry/util/WizardryUtilities.java new file mode 100644 index 00000000..c0912b1f --- /dev/null +++ b/src/main/java/electroblob/wizardry/util/WizardryUtilities.java @@ -0,0 +1,967 @@ +package electroblob.wizardry.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; +import java.util.UUID; +import java.util.function.Function; + +import javax.annotation.Nullable; + +import org.apache.commons.lang3.tuple.ImmutablePair; + +import electroblob.wizardry.CommonProxy; +import electroblob.wizardry.WizardData; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Element; +import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.entity.living.ISummonedCreature; +import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.spell.MindControl; +import electroblob.wizardry.spell.Spell; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityCreeper; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.inventory.EntityEquipmentSlot.Type; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.network.datasync.DataParameter; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumFacing; +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.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.ReflectionHelper; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** This class contains some useful static methods for use anywhere - items, entities, spells, events, blocks, etc. + * @see CommonProxy + * @see WandHelper + * @since Wizardry 1.0 + * @author Electroblob */ +public final class WizardryUtilities { + + /** Constant which is simply an array of the four armour slots. (Could've sworn this exists somewhere in + * vanilla, but I can't find it anywhere...) */ + public static final EntityEquipmentSlot[] ARMOUR_SLOTS; + /** Changed to a constant in wizardry 2.1, since this is a lot more efficient. */ + private static final DataParameter POWERED; + + static { + // The list of slots needs to be mutable. + List slots = new ArrayList(Arrays.asList(EntityEquipmentSlot.values())); + slots.removeIf(slot -> slot.getSlotType() != Type.ARMOR); + ARMOUR_SLOTS = slots.toArray(new EntityEquipmentSlot[0]); + + // Null is passed in deliberately since POWERED is a static field. + POWERED = ReflectionHelper.getPrivateValue(EntityCreeper.class, null, "POWERED", "field_184714_b"); + } + + // SECTION Block/Entity/World Utilities + // =============================================================================================================== + + /** + * Returns whether the block at the given coordinates can be replaced by another one (works as if a block is being placed by a player). + * True for air, liquids, vines, tall grass and snow layers but not for flowers, signs etc. + * This is a shortcut for world.getBlockState(pos).getMaterial().isReplaceable(). + * @see WizardryUtilities#canBlockBeReplacedB(World, BlockPos) + */ + public static boolean canBlockBeReplaced(World world, BlockPos pos){ + return world.isAirBlock(new BlockPos(pos)) || world.getBlockState(pos).getMaterial().isReplaceable(); + } + + /** + * Returns whether the block at the given coordinates can be replaced by another one (works as if a block is being placed by a player) + * and is not a liquid. + * True for air, vines, tall grass and snow layers but not for flowers, signs etc. or any liquids. + * @see WizardryUtilities#canBlockBeReplaced(World, BlockPos) + */ + public static boolean canBlockBeReplacedB(World world, BlockPos pos){ + return canBlockBeReplaced(world, pos) && !world.getBlockState(pos).getMaterial().isLiquid(); + } + + /** + * Returns whether the block at the given coordinates is unbreakable in survival mode. In vanilla this is true for + * bedrock and end portal frame, for example. + * This is a shortcut for world.getBlockState(pos).getBlockHardness(world, pos) == -1.0f. + * Not much of a shortcut any more, since block ids have been phased out. + */ + public static boolean isBlockUnbreakable(World world, BlockPos pos){ + return world.isAirBlock(new BlockPos(pos)) ? false : world.getBlockState(pos).getBlockHardness(world, pos) == -1.0f; + } + + /** + * Finds the nearest floor level to the given y coord within the range specified at the given x and z coords. + * Liquids and other blocks that cannot be built on top of do not count, but stuff like signs does. + * (Technically any block is allowed to be the floor according to the code, but seeing as it searches upwards and + * non-solid blocks usually need a supporting block, the floor is likely to always be solid). + * @param world + * @param x The x coordinate to search in + * @param y The y coordinate to search from + * @param z The z coordinate to search in + * @param range The maximum distance from the given y coordinate to search. + * @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the floor + * as would be seen in the debug screen when the player is standing on it. + * @see WizardryUtilities#getNearestFloorLevelB(World, BlockPos, int) + */ + public static int getNearestFloorLevel(World world, BlockPos pos, int range){ + + int yCoord = -2; + for(int i = -range; i <= range; i++){ + // The last bit determines whether the block found to be a suitable floor is closer than the previous one found. + if(world.isSideSolid(pos.up(i), EnumFacing.UP) && (world.isAirBlock(pos.up(i+1)) || !world.isSideSolid(pos.up(i+1), EnumFacing.UP)) + && (i < yCoord-pos.getY() || yCoord == -2)){ + yCoord = pos.getY() + i; + } + } + return yCoord + 1; + } + + /** + * Finds the nearest floor level to the given y coord within the range specified at the given x and z coords. + * Only works if the block above the floor is actually air and the floor is solid or a liquid. + * @param world + * @param x The x coordinate to search in + * @param y The y coordinate to search from + * @param z The z coordinate to search in + * @param range The maximum distance from the given y coordinate to search. + * @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the floor + * as would be seen in the debug screen when the player is standing on it. + * @see WizardryUtilities#getNearestFloorLevel(World, BlockPos, int) + */ + public static int getNearestFloorLevelB(World world, BlockPos pos, int range){ + int yCoord = -2; + for(int i = -range; i <= range; i++){ + if(world.isAirBlock(new BlockPos(pos.up(i+1))) && (world.getBlockState(pos.up(i)).getMaterial().isLiquid() || world.isSideSolid(pos.up(i), EnumFacing.UP)) + && (i < yCoord-pos.getY() || yCoord == -2)){ + // The last bit determines whether the block found to be a suitable floor is closer than the previous one found. + yCoord = pos.getY() + i; + } + } + return yCoord + 1; + } + + /** + * Finds the nearest floor level to the given y coord within the range specified at the given x and z coords. + * Everything that is not air is treated as floor, even stuff that can't be walked on. + * @param world + * @param x The x coordinate to search in + * @param y The y coordinate to search from + * @param z The z coordinate to search in + * @param range The maximum distance from the given y coordinate to search. + * @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the floor + * as would be seen in the debug screen when the player is standing on it. + * @see WizardryUtilities#getNearestFloorLevel(World, BlockPos, int) + */ + public static int getNearestFloorLevelC(World world, BlockPos pos, int range){ + int yCoord = -2; + for(int i = -range; i <= range; i++){ + if(world.isAirBlock(new BlockPos(pos.up(i+1))) && (i < yCoord-pos.getY() || yCoord == -2)){ + // The last bit determines whether the block found to be a suitable floor is closer than the previous one found. + yCoord = pos.getY() + i; + } + } + return yCoord + 1; + } + + /** + * Gets a random position on the ground near the player within the specified horizontal and vertical ranges. Used + * to find a position to spawn entities in summoning spells. + * @param entity The entity around which to search + * @param horizontalRange The maximum number of blocks on the x or z axis the returned position can be from the + * given entity. The number of operations performed by this method is proportional to the square of this + * parameter, so for performance reasons it is recommended that it does not exceed around 10. + * @param verticalRange The maximum number of blocks on the y axis the returned position can be from the given + * entity + * @return A BlockPos with the coordinates of the block directly above the ground at the position found, or null + * if none were found within range. Importantly, since this method checks all possible positions within + * range (i.e. randomness only occurs when deciding between the possible positions), if it returns null once + * then it will always return null given the same circumstances and parameters. What this means is that you can + * (and should) immediately stop trying to cast a summoning spell if this returns null. + */ + @Nullable + public static BlockPos findNearbyFloorSpace(Entity entity, int horizontalRange, int verticalRange){ + + World world = entity.worldObj; + List possibleLocations = new ArrayList(); + BlockPos origin = new BlockPos(entity); + + for(int x = -horizontalRange; x <= horizontalRange; x++){ + for(int z = -horizontalRange; z <= horizontalRange; z++){ + int y = WizardryUtilities.getNearestFloorLevel(world, origin.add(x, 0, z), verticalRange); + if(y > -1) possibleLocations.add(new BlockPos(origin.getX() + x, y, origin.getZ() + z)); + } + } + + if(possibleLocations.isEmpty()){ + return null; + }else{ + return possibleLocations.get(world.rand.nextInt(possibleLocations.size())); + } + + } + + /** + * Gets the blockstate of the block the specified entity is standing on. Uses {@link MathHelper#floor_double(double)} because casting to int will not + * return the correct coordinate when x or z is negative. + */ + public static IBlockState getBlockEntityIsStandingOn(Entity entity){ + BlockPos pos = new BlockPos(MathHelper.floor_double(entity.posX), (int)entity.getEntityBoundingBox().minY-1, MathHelper.floor_double(entity.posZ)); + return entity.worldObj.getBlockState(pos); + } + + /** + * Shorthand for {@link WizardryUtilities#getEntitiesWithinRadius(double, double, double, double, World, Class)} + * with EntityLivingBase as the entity type. This is by far the most common use for that method, which is why this + * shorthand exists. + * @param radius The search radius + * @param x The x coordinate to search around + * @param y The y coordinate to search around + * @param z The z coordinate to search around + * @param world The world to search in + */ + public static List getEntitiesWithinRadius(double radius, double x, double y, double z, World world){ + return getEntitiesWithinRadius(radius, x, y, z, world, EntityLivingBase.class); + } + + /** + * Returns all entities of the specified type within the specified radius of the given coordinates. This is different to using + * a raw AABB because a raw AABB will search in a cube volume rather than a sphere. + * Note that this does not exclude any entities; if any specific entities are to be excluded this must be + * checked when iterating through the list. + * @see {@link WizardryUtilities#getEntitiesWithinRadius(double, double, double, double, World)} + * @param radius The search radius + * @param x The x coordinate to search around + * @param y The y coordinate to search around + * @param z The z coordinate to search around + * @param world The world to search in + * @param entityType The class of entity to search for; pass in Entity.class for all entities + */ + public static List getEntitiesWithinRadius(double radius, double x, double y, double z, World world, Class entityType){ + AxisAlignedBB aabb = new AxisAlignedBB(x - radius, y - radius, z - radius, x + radius, y + radius, z + radius); + List entityList = world.getEntitiesWithinAABB(entityType, aabb); + for(int i=0;i radius){ + entityList.remove(i); + } + } + return entityList; + } + + /** + * Gets an entity from its UUID. Note that you should check this isn't null. If the UUID is known to belong + * to an EntityPlayer, use the more efficient {@link World#getPlayerEntityByUUID(UUID)} instead. + * @param world The world the entity is in + * @param id The entity's UUID + * @return The Entity that has the given UUID, or null if no such entity exists in the specified world. + */ + @Nullable + public static Entity getEntityByUUID(World world, UUID id){ + + for(Entity entity : world.loadedEntityList){ + // This is a perfect example of where you need to use .equals() and not ==. For most applications, + // this was unnoticeable until world reload because the UUID instance or entity instance is stored. + // Fixed now though. + if(entity.getUniqueID().equals(id)){ + return entity; + } + } + return null; + } + + // No point allowing anything other than players for these methods since other entities can use Entity#playSound. + + /** Shortcut for {@link World#playSound(EntityPlayer, double, double, double, SoundEvent, SoundCategory, float, float)} + * where the player is null but the x, y and z coordinates are those of the passed in player. Use in preference to + * {@link EntityPlayer#playSound(SoundEvent, float, float)} if there are client-server discrepancies. */ + public static void playSoundAtPlayer(EntityPlayer player, SoundEvent sound, SoundCategory category, float volume, float pitch){ + player.worldObj.playSound(null, player.posX, player.posY, player.posZ, sound, category, volume, pitch); + } + + /** See {@link WizardryUtilities#playSoundAtPlayer(EntityPlayer, SoundEvent, SoundCategory, float, float)}. Category + * defaults to {@link SoundCategory#PLAYERS}. */ + public static void playSoundAtPlayer(EntityPlayer player, SoundEvent sound, float volume, float pitch){ + player.worldObj.playSound(null, player.posX, player.posY, player.posZ, sound, SoundCategory.PLAYERS, volume, pitch); + } + + /** + * Returns the entity riding the given entity, or null if there is none. Allows for neater code now that + * entities have a list of passengers, because it is necessary to check that the list is not null or empty first. + */ + @Nullable + public static Entity getRider(Entity entity){ + return entity.getPassengers() != null && !entity.getPassengers().isEmpty() ? entity.getPassengers().get(0) : null; + } + + /** + * Attacks the given entity with the given damage source and amount, but preserving the entity's original velocity + * instead of applying knockback, as would happen with {@link EntityLivingBase#attackEntityFrom(DamageSource, float)} + * (More accurately, calls that method as normal and then resets the entity's velocity to what it was before). + * Handy for when you need to damage an entity repeatedly in a short space of time. + * @param entity The entity to attack + * @param source The source of the damage + * @param amount The amount of damage to apply + * @return True if the attack succeeded, false if not. + */ + public static boolean attackEntityWithoutKnockback(Entity entity, DamageSource source, float amount){ + double vx = entity.motionX; double vy = entity.motionY; double vz = entity.motionZ; + boolean succeeded = entity.attackEntityFrom(source, amount); + entity.motionX = vx; entity.motionY = vy; entity.motionZ = vz; + return succeeded; + } + + /** + * Applies the standard (non-enchanted) amount of knockback to the given target, using the same calculation as + * {@link EntityLivingBase#attackEntityFrom(DamageSource, float)}. Use in conjunction with + * {@link WizardryUtilities#attackEntityWithoutKnockback(Entity, DamageSource, float)} to change the source of + * knockback for an attack. + * @param attacker The entity that caused the knockback; the target will be pushed away from this entity. + * @param target The entity to be knocked back. + */ + public static void applyStandardKnockback(Entity attacker, EntityLivingBase target){ + double dx = attacker.posX - target.posX; + double dz; + for(dz = attacker.posZ - target.posZ; dx * dx + dz * dz < 1.0E-4D; dz = (Math.random() - Math.random()) * 0.01D){ + dx = (Math.random() - Math.random()) * 0.01D; + } + // The first argument is never used. + target.knockBack(null, 0.4f, dx, dz); + } + + // Just what benefit does having posY be the eye position on the first person client actually give? + + /** + * Gets the y coordinate of the given player's eyes. This is to cover an inconsistency between the value of + * EntityPlayer.posY on the first person client and everywhere else; in first person (i.e. when + * Minecraft.getMinecraft().thePlayer == player) player.posY is the eye position, but everywhere else it is the + * feet position. This is intended for use when spawning particles, since this is the only situation where the + * discrepancy is likely to matter. + *

    + * As of Wizardry 1.2, this is just a shorthand for:

    + *

    player.getEntityBoundingBox().minY + player.getEyeHeight()
    + */ + public static double getPlayerEyesPos(EntityPlayer player){ + return player.getEntityBoundingBox().minY + player.getEyeHeight(); + } + + /** Returns a list of the itemstacks in the given player's hotbar. Defined here for convenience and to + * centralise the (unfortunately unavoidable) use of hardcoded numbers to reference the inventory slots. The + * returned list is a modifiable copy of part of the player's inventory stack list; as such, changes to the list + * are not written through to the player's inventory. However, the ItemStack instances themselves are not + * copied, so changes to any of their fields (size, metadata...) will change those in the player's inventory. + * @since Wizardry 1.2 */ + public static List getHotbar(EntityPlayer player){ + return new ArrayList(Arrays.asList(player.inventory.mainInventory).subList(0, 9)); + } + + /** Returns a list of the itemstacks in the given player's hotbar and offhand, sorted into the following order: + * main hand, offhand, rest of hotbar left-to-right. The returned list is a modifiable copy of part of the player's + * inventory stack list; as such, changes to the list are not written through to the player's inventory. + * However, the ItemStack instances themselves are not copied, so changes to any of their fields (size, metadata...) + * will change those in the player's inventory. + * @since Wizardry 1.2 */ + public static List getPrioritisedHotbarAndOffhand(EntityPlayer player){ + List hotbar = WizardryUtilities.getHotbar(player); + // Adds the offhand item to the beginning of the list so it is processed before the hotbar + hotbar.add(0, player.getHeldItemOffhand()); + // Moves the item in the main hand to the beginning of the list so it is processed first + hotbar.remove(player.getHeldItemMainhand()); + hotbar.add(0, player.getHeldItemMainhand()); + return hotbar; + } + + /** Tests whether the specified player has any of the specified item in their entire inventory, including armour + * slots and offhand. */ + public static boolean doesPlayerHaveItem(EntityPlayer player, Item item){ + + for(ItemStack stack : player.inventory.mainInventory){ + if(stack != null && stack.getItem() == item){ + return true; + } + } + + for(ItemStack stack : player.inventory.armorInventory){ + if(stack != null && stack.getItem() == item){ + return true; + } + } + + for(ItemStack stack : player.inventory.offHandInventory){ + if(stack != null && stack.getItem() == item){ + return true; + } + } + + return false; + } + + /** Checks if the given player is opped on the given server. If the server is a singleplayer or LAN server, this + * means they have cheats enabled. */ + public static boolean isPlayerOp(EntityPlayer player, MinecraftServer server){ + return server.getPlayerList().getOppedPlayers().getEntry(player.getGameProfile()) != null; + } + + /** Turns the given creeper into a charged creeper. In 1.10, this requires reflection since the DataManager keys + * are private. (You could call {@link EntityCreeper#onStruckByLightning(...)} and then heal it and + * extinguish it, but that's a bit awkward.) */ + public static void chargeCreeper(EntityCreeper creeper){ + creeper.getDataManager().set(POWERED, true); + } + + // SECTION Raytracing + // =============================================================================================================== + + /** + * Does a block ray trace (NOT entities) from an entity's eyes (i.e. properly...) + */ + @Nullable + public static RayTraceResult rayTrace(double range, World world, EntityLivingBase entity, boolean hitLiquids){ + + Vec3d start = new Vec3d(entity.posX, entity.getEntityBoundingBox().minY + entity.getEyeHeight(), entity.posZ); + Vec3d look = entity.getLookVec(); + Vec3d end = start.addVector(look.xCoord * range, look.yCoord * range, look.zCoord * range); + return world.rayTraceBlocks(start, end, hitLiquids); + } + + /** + * Helper method which does a rayTrace for entities from an entity's eye level in the direction they are looking + * with a specified range, using the tracePath method. Tidies up the code a bit. Border size defaults to 1. + * + * @param world + * @param entity + * @param range + * @return + */ + @Nullable + public static RayTraceResult standardEntityRayTrace(World world, EntityLivingBase entity, double range){ + double dx = entity.getLookVec().xCoord * range; + double dy = entity.getLookVec().yCoord * range; + double dz = entity.getLookVec().zCoord * range; + HashSet hashset = new HashSet(1); + hashset.add(entity); + return WizardryUtilities.tracePath(world, (float)entity.posX, (float)(entity.getEntityBoundingBox().minY + entity.getEyeHeight()), (float)entity.posZ, (float)(entity.posX + dx), (float)(entity.posY + entity.getEyeHeight() + dy), (float)(entity.posZ + dz), 1.0f, hashset, false); + } + + /** + * Helper method which does a rayTrace for entities from a entity's eye level in the direction they are looking + * with a specified range and radius, using the tracePath method. Tidies up the code a bit. + * + * @param world + * @param entity + * @param range + * @param borderSize + * @return + */ + @Nullable + public static RayTraceResult standardEntityRayTrace(World world, EntityLivingBase entity, double range, float borderSize){ + double dx = entity.getLookVec().xCoord * range; + double dy = entity.getLookVec().yCoord * range; + double dz = entity.getLookVec().zCoord * range; + HashSet hashset = new HashSet(1); + hashset.add(entity); + return WizardryUtilities.tracePath(world, (float)entity.posX, (float)(entity.getEntityBoundingBox().minY + entity.getEyeHeight()), (float)entity.posZ, (float)(entity.posX + dx), (float)(entity.posY + entity.getEyeHeight() + dy), (float)(entity.posZ + dz), borderSize, hashset, false); + } + + /** + * Method for ray tracing entities (the useless default method doesn't work, despite EnumHitType having an ENTITY field...) + * You can also use this for seeking. + * + * @param world + * @param x startX + * @param y startY + * @param z startZ + * @param tx endX + * @param ty endY + * @param tz endZ + * @param borderSize extra area to examine around line for entities + * @param excluded any excluded entities (the player, etc) + * @return a RayTraceResult of either the block hit (no entity hit), the entity hit (hit an entity), or null for nothing hit + */ + @Nullable + public static RayTraceResult tracePath(World world, float x, float y, float z, float tx, float ty, float tz, float borderSize, HashSet excluded, boolean collideablesOnly){ + + Vec3d startVec = new Vec3d(x, y, z); + //Vec3d lookVec = new Vec3d(tx-x, ty-y, tz-z); + Vec3d endVec = new Vec3d(tx, ty, tz); + float minX = x < tx ? x : tx; + float minY = y < ty ? y : ty; + float minZ = z < tz ? z : tz; + float maxX = x > tx ? x : tx; + float maxY = y > ty ? y : ty; + float maxZ = z > tz ? z : tz; + AxisAlignedBB bb = new AxisAlignedBB(minX, minY, minZ, maxX, maxY, maxZ).expand(borderSize, borderSize, borderSize); + List allEntities = world.getEntitiesWithinAABBExcludingEntity(null, bb); + RayTraceResult blockHit = world.rayTraceBlocks(startVec, endVec); + startVec = new Vec3d(x, y, z); + endVec = new Vec3d(tx, ty, tz); + float maxDistance = (float) endVec.distanceTo(startVec); + if(blockHit!=null) + { + maxDistance = (float) blockHit.hitVec.distanceTo(startVec); + } + Entity closestHitEntity = null; + float closestHit = maxDistance; + float currentHit = 0.f; + AxisAlignedBB entityBb;// = ent.getBoundingBox(); + RayTraceResult intercept; + for(Entity ent : allEntities) + { + if((ent.canBeCollidedWith() || !collideablesOnly) && ((excluded != null && !excluded.contains(ent)) || excluded == null)) + { + float entBorder = ent.getCollisionBorderSize(); + entityBb = ent.getEntityBoundingBox(); + if(entityBb!=null) + { + entityBb = entityBb.expand(entBorder, entBorder, entBorder); + intercept = entityBb.calculateIntercept(startVec, endVec); + if(intercept!=null) + { + currentHit = (float) intercept.hitVec.distanceTo(startVec); + if(currentHit < closestHit || currentHit==0) + { + closestHit = currentHit; + closestHitEntity = ent; + } + } + } + } + } + if(closestHitEntity!=null) + { + blockHit = new RayTraceResult(closestHitEntity); + } + return blockHit; + } + + // SECTION Rendering and GUIs + // =============================================================================================================== + + // Doesn't seem right to put this in the proxies since it should only ever be called from client-side code, and I'm + // not about to make a whole separate utilities class just for one method. Fully qualified names it is! + /** + * [Client-side only] Draws a textured rectangle, taking the size of the image and the bit needed into + * account, unlike {@link net.minecraft.client.gui.Gui#drawTexturedModalRect(int, int, int, int, int, int) + * Gui.drawTexturedModalRect(int, int, int, int, int, int)}, which is harcoded for only 256x256 textures. Also + * handy for custom potion icons. + * @param x The x position of the rectangle + * @param y The y position of the rectangle + * @param u The x position of the top left corner of the section of the image wanted + * @param v The y position of the top left corner of the section of the image wanted + * @param width The width of the section + * @param height The height of the section + * @param textureWidth The width of the actual image. + * @param textureHeight The height of the actual image. + */ + @SideOnly(Side.CLIENT) + public static void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight){ + + float f = 1F / (float)textureWidth; + float f1 = 1F / (float)textureHeight; + + // Essentially the same as getting the tessellator. For most code, you'll want the tessellator AND the vertexbuffer + // stored in local variables. + net.minecraft.client.renderer.VertexBuffer buffer = net.minecraft.client.renderer.Tessellator.getInstance().getBuffer(); + // Equivalent of tessellator.startDrawingQuads() + buffer.begin(org.lwjgl.opengl.GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); + // Equivalent of tessellator.addVertex() + buffer.pos((double)(x), (double)(y + height), 0).tex((double)((float)(u) * f), (double)((float)(v + height) * f1)).endVertex(); + buffer.pos((double)(x + width), (double)(y + height), 0).tex((double)((float)(u + width) * f), (double)((float)(v + height) * f1)).endVertex(); + buffer.pos((double)(x + width), (double)(y), 0).tex((double)((float)(u + width) * f), (double)((float)(v) * f1)).endVertex(); + buffer.pos((double)(x), (double)(y), 0).tex((double)((float)(u) * f), (double)((float)(v) * f1)).endVertex(); + // Exactly the same as before. + net.minecraft.client.renderer.Tessellator.getInstance().draw(); + } + + /** Shorthand for {@link WizardryUtilities#drawTexturedRect(int, int, int, int, int, int, int, int)} which draws + * the entire texture (u and v are set to 0 and textureWidth and textureHeight are the same as width and height). */ + @SideOnly(Side.CLIENT) + public static void drawTexturedRect(int x, int y, int width, int height){ + drawTexturedRect(x, y, 0, 0, width, height, width, height); + } + + // SECTION NBT and Data Storage + // =============================================================================================================== + + /** + * Verifies that the given string is a valid string representation of a UUID. More specifically, returns true if and + * only if the given string is not null and matches the regular expression:

    + *

    /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/

    + * which is the regex equivalent of the standard string representation of a UUID as described in {@link UUID#toString()}. + * This method is intended to be used as a check to prevent an {@link IllegalArgumentException} from occurring when + * calling {@link UUID#fromString(String)}. + * @param string The string to be checked + * @return Whether the given string is a valid string representation of a UUID + * @deprecated UUIDs can now be stored in NBT directly; use that in preference to storing them as strings. + */ + @Deprecated + public static boolean verifyUUIDString(String string){ + return string != null && string.matches("/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/"); + } + + /** Generic method that stores any Map to an NBTTagList, given two functions that convert the key and value + * types in that map to subclasses of NBTBase. For what it's worth, there is very little point in using this unless + * you can use something more concise than an anonymous class to do the conversion. A lambda expression, or better, + * a method reference, would fit nicely. For example, take ExtendedPlayer's use of this to store conjured item + * durations: + *

    + * properties.setTag("conjuredItems", WizardryUtilities.mapToNBT(this.conjuredItemDurations, + * item -> new NBTTagInt(Item.getIdFromItem((Item)item)), NBTTagInt::new)); + *

    + * This is a lot nicer than simply iterating through the map, because for that you need to use the entry list, + * which introduces local variables that aren't really necessary. Notice that, since the values V in the map are + * simply Integer objects, a simple constructor reference to NBTTagInt::new can be used instead of a lambda + * expression (the Integer is auto-unboxed to int). + * + * @param The type of key stored in the given Map. + * @param The type of value stored in the given Map. + * @param The subtype of NBTBase that the keys (of type K) will be converted to. + * @param The subtype of NBTBase that the values (of type V) will be converted to. + * @param map The Map to be stored. + * @param keyFunction A Function that converts the keys in the map to NBT objects that can be stored. + * @param valueFunction A Function that converts the values in the map to NBT objects that can be stored. + * @param keyTagName The tag name to use for the key tags. + * @param valueTagName The tag name to use for the value tags. + * @return An NBTTagList that represents the given Map. + */ + public static NBTTagList mapToNBT(Map map, Function keyFunction, Function valueFunction, String keyTagName, String valueTagName){ + + NBTTagList tagList = new NBTTagList(); + + for(Entry entry : map.entrySet()){ + NBTTagCompound mapping = new NBTTagCompound(); + mapping.setTag(keyTagName, keyFunction.apply(entry.getKey())); + mapping.setTag(valueTagName, valueFunction.apply(entry.getValue())); + tagList.appendTag(mapping); + } + + return tagList; + } + + /** See {@link WizardryUtilities#mapToNBT(Map, Function, Function, String, String)}; this version is for when the + * names of the individual key/value tags are unimportant (they default to "key" and "value" respectively). */ + public static NBTTagList mapToNBT(Map map, Function keyFunction, Function valueFunction){ + return mapToNBT(map, keyFunction, valueFunction, "key", "value"); + } + + /** Generic method that reads a Map from an NBTTagList, given two functions that convert the key and value tag types + * into the key and value types in the returned map. The given NBTTagList remains unchanged after calling this method. + * + * @param The type of key stored in the returned Map. + * @param The type of value stored in the returned Map. + * @param The subtype of NBTBase that the keys are stored as. + * @param The subtype of NBTBase that the values are stored as. + * @param tagList The NBTTagList to be converted. This must be a list of compound tags. + * @param keyFunction A Function that converts the generic NBTBase tags in the list to keys of type K for the map. + * @param valueFunction A Function that converts the generic NBTBase tags in the list to values of type V for the map. + * @param keyTagName The tag name used for the key tags. + * @param valueTagName The tag name used for the value tags. + * @return A Map containing the keys and values stored in the given NBTTagList. Can be empty, but not null. + * @throws ClassCastException If the tags are not of the expected type. + * @see WizardryUtilities#mapToNBT(Map, Function, Function, String, String) + */ + @SuppressWarnings("unchecked") // Intentional, because throwing an exception is appropriate here. + public static Map NBTToMap(NBTTagList tagList, Function keyFunction, Function valueFunction, String keyTagName, String valueTagName){ + + Map map = new HashMap(); + + for(int i=0; i Map NBTToMap(NBTTagList tagList, Function keyFunction, Function valueFunction){ + return NBTToMap(tagList, keyFunction, valueFunction, "key", "value"); + } + + /** Generic method that stores any Collection to an NBTTagList, given a function that converts the elements in + * that collection to subclasses of NBTBase. For what it's worth, there is very little point in using this unless + * you can use something more concise than an anonymous class to do the conversion. A lambda expression, or better, + * a method reference, would fit nicely. + * + * @param The type of element stored in the given Collection. + * @param The NBT tag type that the elements will be converted to. + * @param list The Collection to be stored. + * @param function A Function that converts the elements in the collection to NBT objects that can be stored. + * @return An NBTTagList that represents the given Collection. + */ + public static NBTTagList listToNBT(Collection list, Function function){ + + NBTTagList tagList = new NBTTagList(); + // If the collection is ordered, it will preserve the order, even though we don't know what type it is yet. + for(E element : list){ + tagList.appendTag(function.apply(element)); + } + + return tagList; + } + + /** Generic method that reads a Collection from an NBTTagList, given a function that converts the element tag + * types to the element types in the returned collection. The given NBTTagList remains unchanged after calling this + * method. Unless the target variable for this method is of type Collection, you will need to create a new + * collection containing the elements in the returned collection via that collection's constructor (e.g. {@code new + * HashSet(collection)}). Although this method returns a Collection rather than any of its subtypes, it + * uses an ArrayList internally to guarantee the order of the elements in the returned collection is the same + * as the order in which they were stored. As such, you may safely cast to List should you wish. + * + * @param The type of element stored in the returned Collection. + * @param The subtype of NBTBase that the elements are stored as. + * @param tagList The NBTTagList to be converted. + * @param function A Function that converts the generic NBTBase tags in the list to elements for the collection. + * Chances are you will need to cast the NBTBase tag to whichever NBT tag type you are expecting in order to + * access the appropriate getter method. + * @return A Collection containing the elements stored in the given NBTTagList. Can be empty, but not null. + * @throws ClassCastException If the tags are not of the expected type. + */ + @SuppressWarnings("unchecked") // Intentional, because throwing an exception is appropriate here. + public static Collection NBTToList(NBTTagList tagList, Function function){ + // Uses an ArrayList to guarantee iteration order, and also to permit duplicate elements (which are + // perfectly reasonable in this context). + Collection list = new ArrayList(); + // The original tag list should remain unchanged, hence the copy. + NBTTagList tagList2 = (NBTTagList) tagList.copy(); + + while(!tagList2.hasNoTags()){ + NBTBase tag = tagList2.removeTag(0); + // Why oh why is NBTTagList not parametrised? It even has a tagType field, so it must know! + try { list.add(function.apply((T)tag)); } catch (ClassCastException e){ + Wizardry.logger.error("Error when reading list from NBT: unexpected tag type " + NBTBase.NBT_TYPES[tag.getId()], e); + } + } + + return list; + + } + + // TODO: Backport: It has recently become apparent that storing UUIDs as strings is not good practice, so backport these two + // methods to 1.7.10 and replace tag.setUniqueId and tag.getUniqueId with their respective contents from 1.10.2. + + /** Returns an NBTTagCompound which contains only the given UUID, stored using + * {@link NBTTagCompound#setUniqueId(String, UUID)}. Allows for neater storage to NBTTagLists. */ + public static NBTTagCompound UUIDtoTagCompound(UUID id){ + NBTTagCompound tag = new NBTTagCompound(); + tag.setUniqueId("uuid", id); + return tag; + } + + /** Wrapper for {@link NBTTagCompound#getUniqueId(String)} which converts an NBTTagCompound directly to a UUID. + * Intended to be used as the inverse of {@link WizardryUtilities#UUIDtoTagCompound(UUID)}.*/ + public static UUID tagCompoundToUUID(NBTTagCompound tag){ + return tag.getUniqueId("uuid"); + } + + // SECTION Ally Designation System + // =============================================================================================================== + + /** + * Returns whether the given target can be attacked by the given attacker. It is up to the caller of this method + * to work out what this means; it doesn't necessarily mean the target is completely immune (for example, revenge + * targeting might reasonably bypass this). This method is intended for use where the damage is indirect and/or + * unavoidable; direct attacks should not check this method. Currently this means the following situations check + * this method: + *

    + * - AI targeting for summoned creatures
    + * - AI targeting for mind-controlled creatures
    + * - Constructs with an area of effect
    + * - Instantaneous spells with an area of effect around the caster (e.g. forest's curse, thunderstorm)
    + * - Any lightning chaining effects
    + * - Any projectiles which seek targets + *

    + * Also note that the friendly fire option is dealt with + * in the event handler. This method acts as a sort of wrapper for all the ADS stuff in {@link WizardData}; more + * details about the ally designation system can be found there. + * + * @param attacker The entity that cast the spell originally + * @param target The entity being attacked + * + * @return False under any of the following circumstances, true otherwise: + *

    + * - Either entity is null + *

    + * - The target is the attacker (this isn't as stupid as it sounds - anything with an AoE might cause this to be + * true, as can summoned creatures) + *

    + * - The target and the attacker are both players and the target is an ally of the attacker (but the + * attacker need not be an ally of the target) + *

    + * - The target is a creature that was summoned/controlled by the attacker or by an ally of the attacker. + */ + public static boolean isValidTarget(Entity attacker, Entity target){ + + if(attacker == null || target == null) return false; + + // Tests whether the target is the attacker + if(target == attacker) return false; + + // Tests whether the target is a creature that was summoned by the attacker + if(target instanceof ISummonedCreature && ((ISummonedCreature)target).getCaster() == attacker){ + return false; + } + + // Tests whether the target is a creature that was mind controlled by the attacker + if(target instanceof EntityLiving && ((EntityLivingBase)target).isPotionActive(WizardryPotions.mind_control)){ + + NBTTagCompound entityNBT = target.getEntityData(); + + if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY)){ + if(attacker == WizardryUtilities.getEntityByUUID(target.worldObj, entityNBT.getUniqueId(MindControl.NBT_KEY))){ + return false; + } + } + } + + // Ally section + if(attacker instanceof EntityPlayer && WizardData.get((EntityPlayer)attacker) != null){ + + if(target instanceof EntityPlayer){ + // Tests whether the target is an ally of the attacker + if(WizardData.get((EntityPlayer)attacker).isPlayerAlly((EntityPlayer)target)){ + return false; + } + + }else if(target instanceof ISummonedCreature){ + // Tests whether the target is a creature that was summoned by an ally of the attacker + if(((ISummonedCreature)target).getCaster() instanceof EntityPlayer + && WizardData.get((EntityPlayer)attacker).isPlayerAlly((EntityPlayer)((ISummonedCreature)target).getCaster())){ + return false; + } + + }else if(target instanceof EntityLiving && ((EntityLivingBase)target).isPotionActive(WizardryPotions.mind_control)){ + // Tests whether the target is a creature that was mind controlled by an ally of the attacker + NBTTagCompound entityNBT = target.getEntityData(); + + if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY)){ + + Entity controller = WizardryUtilities.getEntityByUUID(target.worldObj, entityNBT.getUniqueId(MindControl.NBT_KEY)); + + if(controller instanceof EntityPlayer && WizardData.get((EntityPlayer)attacker).isPlayerAlly((EntityPlayer)controller)){ + return false; + } + } + } + } + + return true; + } + + /** Helper method for testing if the second player is an ally of the first player. Makes the code neater. */ + public static boolean isPlayerAlly(EntityPlayer allyOf, EntityPlayer possibleAlly) { + + WizardData properties = WizardData.get(allyOf); + + if(properties != null && properties.isPlayerAlly(possibleAlly)) return true; + + return false; + } + + // SECTION Loot and Weighting + // =============================================================================================================== + + /** + * See {@link WizardryUtilities#getStandardWeightedRandomSpellId(Random, boolean)}. nonContinuous defaults to false. + */ + public static int getStandardWeightedRandomSpellId(Random random){ + return getStandardWeightedRandomSpellId(random, false); + } + + /** + * Helper method which gets a spell id according to the standard weighting. The tier is a weighted random value; + * the actual spell within that tier is completely random. Will not return the id of a spell which has been + * disabled in the config. This is for simple stuff like chests and drops; more complex generators like wizard + * trades don't use this method. + *

    + * For reference, the standard weighting is as follows: + * Basic: 60%, Apprentice: 25%, Advanced: 10%, Master: 5% + * + * @param random An instance of {@link Random} to use for RNG + * @param nonContinuous Whether the spells must be non-continuous (used for scrolls) + * @return A random spell id number + */ + public static int getStandardWeightedRandomSpellId(Random random, boolean nonContinuous){ + + Tier tier = Tier.getWeightedRandomTier(random); + + List spells = Spell.getSpells(new Spell.TierElementFilter(tier, null)); + if(nonContinuous) spells.retainAll(Spell.getSpells(Spell.nonContinuousSpells)); + + // Ensures the tier chosen actually has spells in it, and if not uses BASIC instead. + if(spells.isEmpty()){ + spells = Spell.getSpells(new Spell.TierElementFilter(Tier.BASIC, null)); + if(nonContinuous) spells.retainAll(Spell.getSpells(Spell.nonContinuousSpells)); + } + + // Finds a random spell in the list and returns its id. + return spells.get(random.nextInt(spells.size())).id(); + } + + // TODO: These methods need a rethink. What are we trying to achieve with them? Should each use case look in the same + // pool of items? For example, might we (or someone else) want to have a wand which can generate in chests, but is + // not used by wizards? + + // I reckon this should be strictly for cases where we only ever want the standard wand set, i.e. wizards' gear, etc. + + /** + * Helper method to return the appropriate armour item based on element and slot. As of Wizardry 2.1, this uses + * the immutable map stored in {@link WizardryItems#ARMOUR_MAP}. + * Currently used to iterate through armour for registering charging recipes and for chest generation. + * @param element The EnumElement of the armour required. Null will be converted to {@link Element#MAGIC}. + * @param slot EntityEquipmentSlot of the armour piece required + * @return The armour item which corresponds to the given element and slot, or null if no such item exists. + * @throws IllegalArgumentException if the given slot is not an armour slot. + */ + public static Item getArmour(Element element, EntityEquipmentSlot slot){ + if(slot == null || slot.getSlotType() != Type.ARMOR) throw new IllegalArgumentException("Must be a valid armour slot"); + if(element == null) element = Element.MAGIC; + return WizardryItems.ARMOUR_MAP.get(ImmutablePair.of(slot, element)); + } + + /** + * Helper method to return the appropriate wand based on tier and element.As of Wizardry 2.1, this uses + * the immutable map stored in {@link WizardryItems#WAND_MAP}. + * Currently used in the packet handler for upgrading wands, for chest generation and to iterate through + * wands for charging recipes. + * @param tier The tier of the wand required. + * @param element The element of the wand required. Null will be converted to {@link Element#MAGIC}. + * @return The wand item which corresponds to the given element and slot, or null if no such item exists. + * @throws NullPointerException if the given tier is null. + */ + public static Item getWand(Tier tier, Element element){ + if(tier == null) throw new NullPointerException("The given tier cannot be null."); + return WizardryItems.WAND_MAP.get(ImmutablePair.of(tier, element)); + } +} diff --git a/src/main/resources/assets/wizardry/blockstates/arcane_workbench.json b/src/main/resources/assets/wizardry/blockstates/arcane_workbench.json new file mode 100644 index 00000000..c5741b8e --- /dev/null +++ b/src/main/resources/assets/wizardry/blockstates/arcane_workbench.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "wizardry:arcane_workbench" } + } +} diff --git a/src/main/resources/assets/wizardry/blockstates/crystal_block.json b/src/main/resources/assets/wizardry/blockstates/crystal_block.json new file mode 100644 index 00000000..d5ed94e1 --- /dev/null +++ b/src/main/resources/assets/wizardry/blockstates/crystal_block.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "wizardry:crystal_block" } + } +} diff --git a/src/main/resources/assets/wizardry/blockstates/crystal_flower.json b/src/main/resources/assets/wizardry/blockstates/crystal_flower.json new file mode 100644 index 00000000..61e2d72c --- /dev/null +++ b/src/main/resources/assets/wizardry/blockstates/crystal_flower.json @@ -0,0 +1,5 @@ +{ + "variants": { + "normal": { "model": "wizardry:crystal_flower" } + } +} diff --git a/src/main/resources/assets/wizardry/blockstates/crystal_ore.json b/src/main/resources/assets/wizardry/blockstates/crystal_ore.json new file mode 100644 index 00000000..234c9a97 --- /dev/null +++ b/src/main/resources/assets/wizardry/blockstates/crystal_ore.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "wizardry:crystal_ore" } + } +} diff --git a/src/main/resources/assets/wizardry/blockstates/ice_statue.json b/src/main/resources/assets/wizardry/blockstates/ice_statue.json new file mode 100644 index 00000000..e2c0b099 --- /dev/null +++ b/src/main/resources/assets/wizardry/blockstates/ice_statue.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "wizardry:ice_statue" } + } +} diff --git a/src/main/resources/assets/wizardry/blockstates/magic_light.json b/src/main/resources/assets/wizardry/blockstates/magic_light.json new file mode 100644 index 00000000..df809b83 --- /dev/null +++ b/src/main/resources/assets/wizardry/blockstates/magic_light.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "wizardry:magic_light" } + } +} diff --git a/src/main/resources/assets/wizardry/blockstates/meteor.json b/src/main/resources/assets/wizardry/blockstates/meteor.json new file mode 100644 index 00000000..f1d33b5b --- /dev/null +++ b/src/main/resources/assets/wizardry/blockstates/meteor.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "wizardry:meteor" } + } +} diff --git a/src/main/resources/assets/wizardry/blockstates/petrified_stone.json b/src/main/resources/assets/wizardry/blockstates/petrified_stone.json new file mode 100644 index 00000000..c8495ade --- /dev/null +++ b/src/main/resources/assets/wizardry/blockstates/petrified_stone.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "wizardry:petrified_stone" } + } +} diff --git a/src/main/resources/assets/wizardry/blockstates/snare.json b/src/main/resources/assets/wizardry/blockstates/snare.json new file mode 100644 index 00000000..594eb529 --- /dev/null +++ b/src/main/resources/assets/wizardry/blockstates/snare.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "wizardry:snare" } + } +} diff --git a/src/main/resources/assets/wizardry/blockstates/spectral_block.json b/src/main/resources/assets/wizardry/blockstates/spectral_block.json new file mode 100644 index 00000000..79efd6b9 --- /dev/null +++ b/src/main/resources/assets/wizardry/blockstates/spectral_block.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "wizardry:spectral_block" } + } +} diff --git a/src/main/resources/assets/wizardry/blockstates/transportation_stone.json b/src/main/resources/assets/wizardry/blockstates/transportation_stone.json new file mode 100644 index 00000000..67830c59 --- /dev/null +++ b/src/main/resources/assets/wizardry/blockstates/transportation_stone.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "wizardry:transportation_stone" } + } +} diff --git a/src/main/resources/assets/wizardry/blockstates/vanishing_cobweb.json b/src/main/resources/assets/wizardry/blockstates/vanishing_cobweb.json new file mode 100644 index 00000000..ce86f118 --- /dev/null +++ b/src/main/resources/assets/wizardry/blockstates/vanishing_cobweb.json @@ -0,0 +1,6 @@ +{ + "forge_marker": 1, + "variants": { + "normal": { "model": "wizardry:vanishing_cobweb" } + } +} diff --git a/src/main/resources/assets/wizardry/lang/en_GB.lang b/src/main/resources/assets/wizardry/lang/en_GB.lang new file mode 100644 index 00000000..fc660274 --- /dev/null +++ b/src/main/resources/assets/wizardry/lang/en_GB.lang @@ -0,0 +1,738 @@ +tile.wizardry:arcane_workbench.name=Arcane Workbench +tile.wizardry:crystal_ore.name=Crystal Ore +tile.wizardry:petrified_stone.name=Petrified Stone +tile.wizardry:ice_statue.name=Ice Statue +tile.wizardry:crystal_flower.name=Crystal Flower +tile.wizardry:snare.name=Snare +tile.wizardry:transportation_stone.name=Stone of Transportation +tile.wizardry:spectral_block.name=Spectral Block +tile.wizardry:crystal_block.name=Block of Crystal + +item.wizardry:magic_crystal.name=Magic Crystal +item.wizardry:magic_wand.name=Magic Wand +item.wizardry:apprentice_wand.name=Apprentice Wand +item.wizardry:advanced_wand.name=Advanced Wand +item.wizardry:master_wand.name=Master Wand +item.wizardry:spell_book.name=Spell Book + +item.wizardry:arcane_tome.name=Tome of Arcana +item.wizardry:arcane_tome.desc1=Upgrades any %1$s +item.wizardry:arcane_tome.desc2=wand to %1$s tier + +item.wizardry:wizard_handbook.name=The Wizard's Handbook +item.wizardry:wizard_handbook.desc=by %1$s + +item.wizardry:wand.buff=+%1$s %2$s potency +item.wizardry:wand.spell=Current Spell: %1$s +item.wizardry:wand.mana=Mana: %1$s/%2$s + +item.wizardry:wand.addally=%1$s has been added to your list of allies +item.wizardry:wand.removeally=%1$s has been removed from your list of allies + +item.wizardry:basic_fire_wand.name=Wand of Embers +item.wizardry:basic_ice_wand.name=Wand of Frost +item.wizardry:basic_lightning_wand.name=Wand of Sparks +item.wizardry:basic_necromancy_wand.name=Wand of Shadows +item.wizardry:basic_earth_wand.name=Wand of the Forest +item.wizardry:basic_sorcery_wand.name=Wand of Mystery +item.wizardry:basic_healing_wand.name=Wand of Healing + +item.wizardry:apprentice_fire_wand.name=Apprentice Pyromancer Wand +item.wizardry:apprentice_ice_wand.name=Apprentice Ice Mage Wand +item.wizardry:apprentice_lightning_wand.name=Apprentice Storm Mage Wand +item.wizardry:apprentice_necromancy_wand.name=Apprentice Necromancer Wand +item.wizardry:apprentice_earth_wand.name=Apprentice Earth Mage Wand +item.wizardry:apprentice_sorcery_wand.name=Apprentice Sorcerer Wand +item.wizardry:apprentice_healing_wand.name=Apprentice Healer Wand + +item.wizardry:advanced_fire_wand.name=Wand of the Pyromancer +item.wizardry:advanced_ice_wand.name=Wand of the Ice Mage +item.wizardry:advanced_lightning_wand.name=Wand of the Storm Mage +item.wizardry:advanced_necromancy_wand.name=Wand of the Necromancer +item.wizardry:advanced_earth_wand.name=Wand of the Earth Mage +item.wizardry:advanced_sorcery_wand.name=Wand of the Sorcerer +item.wizardry:advanced_healing_wand.name=Wand of the Healer + +item.wizardry:master_fire_wand.name=Master Pyromancer Wand +item.wizardry:master_ice_wand.name=Master Ice Mage Wand +item.wizardry:master_lightning_wand.name=Master Storm Mage Wand +item.wizardry:master_necromancy_wand.name=Master Necromancer Wand +item.wizardry:master_earth_wand.name=Master Earth Mage Wand +item.wizardry:master_sorcery_wand.name=Master Sorcerer Wand +item.wizardry:master_healing_wand.name=Master Healer Wand + +item.wizardry:spectral_sword.name=Spectral Sword +item.wizardry:spectral_pickaxe.name=Spectral Pickaxe +item.wizardry:spectral_bow.name=Spectral Bow + +item.wizardry:mana_flask.name=Mana Flask +item.wizardry:storage_upgrade.name=Wand Storage Upgrade +item.wizardry:siphon_upgrade.name=Wand Siphon Upgrade +item.wizardry:condenser_upgrade.name=Wand Condenser Upgrade +item.wizardry:range_upgrade.name=Wand Range Upgrade +item.wizardry:duration_upgrade.name=Wand Duration Upgrade +item.wizardry:cooldown_upgrade.name=Wand Cooldown Upgrade +item.wizardry:blast_upgrade.name=Wand Blast Upgrade +item.wizardry:attunement_upgrade.name=Wand Attunement Upgrade + +item.wizardry:flaming_axe.name=Flaming Axe +item.wizardry:frost_axe.name=Frost Axe + +item.wizardry:firebomb.name=Firebomb +item.wizardry:poison_bomb.name=Poison Bomb +item.wizardry:smoke_bomb.name=Smoke Bomb + +item.wizardry:blank_scroll.name=Blank Scroll +item.wizardry:scroll.name=Scroll of %1$s +item.wizardry:scroll.undiscovered.name=Scroll "%1$s" +item.wizardry:identification_scroll.name=Scroll of Identification +item.wizardry:identification_scroll.desc1=%1$sIdentifies an unknown +item.wizardry:identification_scroll.desc2=%1$sspell book or scroll +item.wizardry:identification_scroll.nothing_to_identify=Nothing to identify! + +item.wizardry:armour_upgrade.name=Arcane Seal of Protection +item.wizardry:armour_upgrade.desc1=%1$sUpgrades any wizard armour +item.wizardry:armour_upgrade.desc2=%1$sto make it %2$slegendary + +item.wizardry:magic_silk.name=Magical Silk + +item.wizardry:wizard_armour.legendary=Legendary +item.wizardry:wizard_armour.buff=-%1$s %2$s cost +item.wizardry:wizard_armour.mana=Mana: %1$s/%2$s + +item.wizardry:wizard_hat.name=Wizard Hat +item.wizardry:wizard_robe.name=Wizard Robes +item.wizardry:wizard_leggings.name=Wizard Leggings +item.wizardry:wizard_boots.name=Wizard Boots + +item.wizardry:wizard_hat_fire.name=Pyromancer Hat +item.wizardry:wizard_robe_fire.name=Pyromancer Robes +item.wizardry:wizard_leggings_fire.name=Pyromancer Leggings +item.wizardry:wizard_boots_fire.name=Pyromancer Boots + +item.wizardry:wizard_hat_ice.name=Ice Mage Hat +item.wizardry:wizard_robe_ice.name=Ice Mage Robes +item.wizardry:wizard_leggings_ice.name=Ice Mage Leggings +item.wizardry:wizard_boots_ice.name=Ice Mage Boots + +item.wizardry:wizard_hat_lightning.name=Storm Mage Hat +item.wizardry:wizard_robe_lightning.name=Storm Mage Robes +item.wizardry:wizard_leggings_lightning.name=Storm Mage Leggings +item.wizardry:wizard_boots_lightning.name=Storm Mage Boots + +item.wizardry:wizard_hat_necromancy.name=Necromancer Hat +item.wizardry:wizard_robe_necromancy.name=Necromancer Robes +item.wizardry:wizard_leggings_necromancy.name=Necromancer Leggings +item.wizardry:wizard_boots_necromancy.name=Necromancer Boots + +item.wizardry:wizard_hat_earth.name=Earth Mage Hat +item.wizardry:wizard_robe_earth.name=Earth Mage Robes +item.wizardry:wizard_leggings_earth.name=Earth Mage Leggings +item.wizardry:wizard_boots_earth.name=Earth Mage Boots + +item.wizardry:wizard_hat_sorcery.name=Sorcerer Hat +item.wizardry:wizard_robe_sorcery.name=Sorcerer Robes +item.wizardry:wizard_leggings_sorcery.name=Sorcerer Leggings +item.wizardry:wizard_boots_sorcery.name=Sorcerer Boots + +item.wizardry:wizard_hat_healing.name=Healer Hat +item.wizardry:wizard_robe_healing.name=Healer Robes +item.wizardry:wizard_leggings_healing.name=Healer Leggings +item.wizardry:wizard_boots_healing.name=Healer Boots + +item.wizardry:spawn_wizard.name=Spawn Wizard +item.wizardry:spawn_evil_wizard.name=Spawn Evil Wizard + +item.wizardry:spectral_helmet.name=Spectral Helmet +item.wizardry:spectral_chestplate.name=Spectral Chestplate +item.wizardry:spectral_leggings.name=Spectral Leggings +item.wizardry:spectral_boots.name=Spectral Boots + +entity.wizardry.summonedcreature.nameplate=%1$s's %2$s +entity.wizardry.summonedcreature.nameplate_fallback=Someone's %1$s + +entity.wizardry.zombie_minion.name=Zombie +entity.wizardry.skeleton_minion.name=Skeleton +entity.wizardry.spider_minion.name=Spider +entity.wizardry.blaze_minion.name=Blaze +entity.wizardry.ice_wraith.name=Ice Wraith +entity.wizardry.lightning_wraith.name=Lightning Wraith +entity.wizardry.shadow_wraith.name=Shadow Wraith +entity.wizardry.spirit_wolf.name=Spirit Wolf +entity.wizardry.spirit_horse.name=Spirit Horse +entity.wizardry.ice_giant.name=Ice Giant +entity.wizardry.phoenix.name=Phoenix +entity.wizardry.wizard.name=Wizard +entity.wizardry.magic_slime.name=Magical Slime +entity.wizardry.silverfish_minion.name=Silverfish +entity.wizardry.storm_elemental.name=Storm Elemental +entity.wizardry.evil_wizard.name=Wizard +entity.wizardry.decoy.name=Decoy + +entity.wizardry.magic_missile.name=Magic +entity.wizardry.arc.name=Magic +entity.wizardry.spark_bomb.name=Magic +entity.wizardry.ice_shard.name=Magic +entity.wizardry.firebomb.name=Magic +entity.wizardry.poison_bomb.name=Magic +entity.wizardry.force_orb.name=Magic +entity.wizardry.spark.name=Magic +entity.wizardry.darkness_orb.name=Magic +entity.wizardry.fire_sigil.name=Magic +entity.wizardry.frost_sigil.name=Magic +entity.wizardry.lightning_sigil.name=Magic +entity.wizardry.lightning_arrow.name=Magic +entity.wizardry.firebolt.name=Magic +entity.wizardry.ice_charge.name=Magic +entity.wizardry.force_arrow.name=Magic +entity.wizardry.dart.name=Magic +entity.wizardry.lightning_disc.name=Magic +entity.wizardry.thunderbolt.name=Magic +entity.wizardry.decay.name=Magic +entity.wizardry.ice_lance.name=Magic +entity.wizardry.smoke_bomb.name=Magic +entity.wizardry.ice_spike.name=Magic + +entity.wizardry.black_hole.name=Black Hole +entity.wizardry.shield.name=Shield +entity.wizardry.meteor.name=Meteor +entity.wizardry.blizzard.name=Blizzard +entity.wizardry.bubble.name=Bubble +entity.wizardry.tornado.name=Tornado +entity.wizardry.lightning_hammer.name=Lightning Hammer +entity.wizardry.arrow_rain.name=Arrow Rain +entity.wizardry.healing_aura.name=Healing Aura +entity.wizardry.forcefield.name=Forcefield +entity.wizardry.ring_of_fire.name=Ring of Fire +entity.wizardry.earthquake.name=Earthquake +entity.wizardry.falling_grass.name=Falling Grass +entity.wizardry.hailstorm.name=Hailstorm +entity.wizardry.lightning_pulse.name=Lightning Pulse + +item_group.wizardry=Wizardry +item_group.wizardryspells=Spells + +achievement.crystal=A Curious Crystal... +achievement.crystal.desc=Mine a magic crystal +achievement.arcane_initiate=Arcane Initiate +achievement.arcane_initiate.desc=Craft a magic wand with a gold nugget, a stick and a magic crystal +achievement.apprentice=Wizard's Apprentice +achievement.apprentice.desc=Use a tome of arcana to upgrade your wand +achievement.master=Arcane Master +achievement.master.desc=Obtain a master wand +achievement.all_spells=Mage of All Trades +achievement.all_spells.desc=Cast every single spell in the game +achievement.wizard_trade=Magic Dealing +achievement.wizard_trade.desc=Purchase an item from a wizard +achievement.buy_master_spell=Knowledge is Power +achievement.buy_master_spell.wizardry:desc=Purchase a master spell from a wizard +achievement.freeze_blaze=Not So Hot Now +achievement.freeze_blaze.desc=Freeze a blaze solid +achievement.charge_creeper=It's Gonna Blow +achievement.charge_creeper.desc='Accidentally' charge a creeper +achievement.frankenstein=Frankenstein +achievement.frankenstein.desc=Turn a pig into a zombie pigman using the lightning bolt spell +achievement.special_upgrade=Arcane Tinkering +achievement.special_upgrade.desc=Apply a special upgrade to a wand +achievement.craft_flask=It's Magic, Bottled! +achievement.craft_flask.desc=Craft a mana flask +achievement.elemental=Elemental +achievement.elemental.desc=Obtain an elemental wand +achievement.armour_set=Now You're a Proper Wizard +achievement.armour_set.desc=Craft and equip a full set of wizard armour +achievement.legendary=Legendary +achievement.legendary.desc=Obtain a piece of legendary wizard armour +achievement.self_destruct=That Backfired +achievement.self_destruct.desc=Get killed by your own magic +achievement.pig_tornado=Not Again... +achievement.pig_tornado.desc=Ride a pig into a tornado +achievement.jam_wizard=Jamming Session +achievement.jam_wizard.desc=Use the arcane jammer spell on a wizard +achievement.slime_skeleton=Sticky Situation +achievement.slime_skeleton.desc=Engulf a skeleton in slime +achievement.anger_wizard=You'll Regret That +achievement.anger_wizard.desc=Make a wizard angry +achievement.defeat_evil_wizard=Righteousness +achievement.defeat_evil_wizard.desc=Defeat an evil wizard +achievement.max_out_wand=Fully Equipped +achievement.max_out_wand.desc=Apply the maximum number of upgrades to a master wand +achievement.element_master=Element Mastery +achievement.element_master.desc=Cast all the spells of any element +achievement.identify_spell=Arcane Appraisal +achievement.identify_spell.wizardry:desc=Use a scroll of identification to identify a spell book or scroll + +tile.wizardry:transportation_stone.confirm=You will now be returned here upon casting %1$s +tile.wizardry:transportation_stone.invalid=You must make a circle with 8 stones of transportation first! + +container.wizardry:arcane_workbench=Arcane Workbench +container.wizardry:arcane_workbench.apply=Apply +container.wizardry:arcane_workbench.mana=Mana: +container.wizardry:arcane_workbench.upgrades=Applied Upgrades: + +tier.basic=Novice +tier.apprentice=Apprentice +tier.advanced=Advanced +tier.master=Master + +element.simple=None +element.fire=Fire +element.ice=Ice +element.lightning=Lightning +element.necromancy=Necromancy +element.earth=Earth +element.sorcery=Sorcery +element.healing=Healing + +element.simple.wizard=Wizard +element.fire.wizard=Pyromancer +element.ice.wizard=Ice Mage +element.lightning.wizard=Storm Mage +element.necromancy.wizard=Necromancer +element.earth.wizard=Earth Mage +element.sorcery.wizard=Sorcerer +element.healing.wizard=Healer + +spelltype.attack=Attack +spelltype.defence=Defence +spelltype.utility=Utility +spelltype.minion=Minion + +spell.disabled=%1$s has been disabled in the config +spell.resist=%1$s resisted %2$s +spell.discover=Discovered the spell %1$s! + +spell.wizardry:agility=Agility +spell.wizardry:arc=Arc +spell.wizardry:arcane_jammer=Arcane Jammer +spell.wizardry:arrow_rain=Arrow Rain +spell.wizardry:banish=Banish +spell.wizardry:black_hole=Black Hole +spell.wizardry:blink=Blink +spell.wizardry:blizzard=Blizzard +spell.wizardry:bubble=Bubble +spell.wizardry:chain_lightning=Chain Lightning +spell.wizardry:clairvoyance=Clairvoyance +spell.wizardry:cobwebs=Cobwebs +spell.wizardry:conjure_armour=Conjure Armour +spell.wizardry:conjure_bow=Conjure Bow +spell.wizardry:conjure_pickaxe=Conjure Pickaxe +spell.wizardry:conjure_sword=Conjure Sword +spell.wizardry:cure_effects=Cure Effects +spell.wizardry:curse_of_soulbinding=Curse of Soulbinding +spell.wizardry:darkness_orb=Darkness Orb +spell.wizardry:darkvision=Darkvision +spell.wizardry:dart=Dart +spell.wizardry:decay=Decay +spell.wizardry:decoy=Decoy +spell.wizardry:detonate=Detonate +spell.wizardry:diamondflesh=Diamondflesh +spell.wizardry:earthquake=Earthquake +spell.wizardry:entrapment=Entrapment +spell.wizardry:fireball=Fireball +spell.wizardry:firebolt=Firebolt +spell.wizardry:firebomb=Firebomb +spell.wizardry:fire_resistance=Fire Resistance +spell.wizardry:fire_sigil=Fire Sigil +spell.wizardry:fireskin=Fireskin +spell.wizardry:firestorm=Firestorm +spell.wizardry:flame_ray=Flame Ray +spell.wizardry:flaming_axe=Flaming Axe +spell.wizardry:flaming_weapon=Flaming Weapon +spell.wizardry:flight=Flight +spell.wizardry:font_of_mana=Font of Mana +spell.wizardry:font_of_vitality=Font of Vitality +spell.wizardry:force_arrow=Force Arrow +spell.wizardry:forcefield=Forcefield +spell.wizardry:force_orb=Force Orb +spell.wizardry:forests_curse=Forest's Curse +spell.wizardry:freeze=Freeze +spell.wizardry:freezing_weapon=Freezing Weapon +spell.wizardry:frost_axe=Frost Axe +spell.wizardry:frost_ray=Frost Ray +spell.wizardry:frost_sigil=Frost Sigil +spell.wizardry:glide=Glide +spell.wizardry:greater_fireball=Greater Fireball +spell.wizardry:greater_heal=Greater Heal +spell.wizardry:group_heal=Group Heal +spell.wizardry:growth_aura=Growth Aura +spell.wizardry:hailstorm=Hailstorm +spell.wizardry:heal=Heal +spell.wizardry:heal_ally=Heal Ally +spell.wizardry:healing_aura=Healing Aura +spell.wizardry:homing_spark=Homing Spark +spell.wizardry:ice_age=Ice Age +spell.wizardry:ice_charge=Ice Charge +spell.wizardry:ice_lance=Ice Lance +spell.wizardry:ice_shard=Ice Shard +spell.wizardry:ice_shroud=Ice Shroud +spell.wizardry:ice_spikes=Ice Spikes +spell.wizardry:ice_statue=Ice Statue +spell.wizardry:ignite=Ignite +spell.wizardry:imbue_weapon=Imbue Weapon +spell.wizardry:intimidate=Intimidate +spell.wizardry:invigorating_presence=Invigorating Presence +spell.wizardry:invisibility=Invisibility +spell.wizardry:invoke_weather=Invoke Weather +spell.wizardry:ironflesh=Ironflesh +spell.wizardry:leap=Leap +spell.wizardry:levitation=Levitation +spell.wizardry:life_drain=Life Drain +spell.wizardry:light=Light +spell.wizardry:lightning_arrow=Lightning Arrow +spell.wizardry:lightning_bolt=Lightning Bolt +spell.wizardry:lightning_disc=Lightning Disc +spell.wizardry:lightning_hammer=Lightning Hammer +spell.wizardry:lightning_pulse=Lightning Pulse +spell.wizardry:lightning_ray=Lightning Ray +spell.wizardry:lightning_sigil=Lightning Sigil +spell.wizardry:lightning_web=Lightning Web +spell.wizardry:magic_missile=Magic Missile +spell.wizardry:metamorphosis=Metamorphosis +spell.wizardry:meteor=Meteor +spell.wizardry:mind_control=Mind Control +spell.wizardry:mind_trick=Mind Trick +spell.wizardry:none=[Empty Slot] +spell.wizardry:oakflesh=Oakflesh +spell.wizardry:petrify=Petrify +spell.wizardry:phase_step=Phase Step +spell.wizardry:plague_of_darkness=Plague of Darkness +spell.wizardry:pocket_furnace=Pocket Furnace +spell.wizardry:pocket_workbench=Pocket Workbench +spell.wizardry:poison=Poison +spell.wizardry:poison_bomb=Poison Bomb +spell.wizardry:replenish_hunger=Replenish Hunger +spell.wizardry:ring_of_fire=Ring of Fire +spell.wizardry:shadow_ward=Shadow Ward +spell.wizardry:shield=Shield +spell.wizardry:shockwave=Shockwave +spell.wizardry:silverfish_swarm=Silverfish Swarm +spell.wizardry:sixth_sense=Sixth Sense +spell.wizardry:slime=Slime +spell.wizardry:smoke_bomb=Smoke Bomb +spell.wizardry:snare=Snare +spell.wizardry:snowball=Snowball +spell.wizardry:spark_bomb=Spark Bomb +spell.wizardry:spectral_pathway=Spectral Pathway +spell.wizardry:spider_swarm=Spider Swarm +spell.wizardry:static_aura=Static Aura +spell.wizardry:summon_blaze=Summon Blaze +spell.wizardry:summon_ice_giant=Summon Ice Giant +spell.wizardry:summon_ice_wraith=Summon Ice Wraith +spell.wizardry:summon_iron_golem=Summon Iron Golem +spell.wizardry:summon_lightning_wraith=Summon Lightning Wraith +spell.wizardry:summon_phoenix=Summon Phoenix +spell.wizardry:summon_shadow_wraith=Summon Shadow Wraith +spell.wizardry:summon_skeleton=Summon Skeleton +spell.wizardry:summon_skeleton_legion=Summon Skeleton Legion +spell.wizardry:summon_snow_golem=Summon Snow Golem +spell.wizardry:summon_spirit_horse=Summon Spirit Horse +spell.wizardry:summon_spirit_wolf=Summon Spirit Wolf +spell.wizardry:summon_storm_elemental=Summon Storm Elemental +spell.wizardry:summon_wither_skeleton=Summon Wither Skeleton +spell.wizardry:summon_zombie=Summon Zombie +spell.wizardry:telekinesis=Telekinesis +spell.wizardry:thunderbolt=Thunderbolt +spell.wizardry:thunderstorm=Thunderstorm +spell.wizardry:tornado=Tornado +spell.wizardry:transience=Transience +spell.wizardry:transportation=Transportation +spell.wizardry:vanishing_box=Vanishing Box +spell.wizardry:wall_of_frost=Wall of Frost +spell.wizardry:water_breathing=Water Breathing +spell.wizardry:whirlwind=Whirlwind +spell.wizardry:wither=Wither +spell.wizardry:wither_skull=Wither Skull + +spell.wizardry:agility.desc=Grants the caster faster movement speed and greater jump height for 30 seconds. +spell.wizardry:arc.desc=Fires a spark of lightning at the target. +spell.wizardry:arcane_jammer.desc=Prevents the target from using magic for 15 seconds. +spell.wizardry:arrow_rain.desc="Archers, fire!" +spell.wizardry:banish.desc=Teleports the target against its will to a random location within a certain range. +spell.wizardry:black_hole.desc=Tear reality asunder. +spell.wizardry:blink.desc=Teleports the caster over a short distance to where they are pointing. +spell.wizardry:blizzard.desc=Creates a zone of swirling icy wind which slows and continually damages anything trapped inside. The caster is immune to the damage but is still slowed. +spell.wizardry:bubble.desc=Fires a jet of bubbles which causes anything it hits to float upwards helplessly. The target will fall after a certain time or if it is damaged. +spell.wizardry:chain_lightning.desc=Fires a spark of lightning at the target, which then chains to additional targets up to twice. +spell.wizardry:clairvoyance.desc=Reveals the path to a remembered location. With this spell selected, sneak-right-click on a block to set the location. Cast this spell normally to reveal the path. The path will fade after 90 seconds. +spell.wizardry:cobwebs.desc=Creates cobwebs where you are pointing, which greatly hamper the movement of any creatures caught amongst them. The cobwebs will vanish after 20 seconds or if broken. +spell.wizardry:conjure_armour.desc=Creates spectral armour around the caster which offers protection equal to that of iron armour. The armour lasts for 60 seconds. The caster must have an empty armour slot. +spell.wizardry:conjure_bow.desc=Creates a spectral bow with unlimited arrows that lasts for 30 seconds. +spell.wizardry:conjure_pickaxe.desc=Creates a spectral pickaxe of equal strength to an iron pickaxe that lasts for 30 seconds. +spell.wizardry:conjure_sword.desc=Creates a spectral sword of equal strength to an iron sword that lasts for 30 seconds. +spell.wizardry:cure_effects.desc=Removes all potion effects currently affecting the caster, good or bad. +spell.wizardry:curse_of_soulbinding.desc=Causes the target's soul to be inextricably bound to that of the caster, meaning all damage dealt to the caster is also dealt to the victim. Lasts until either the victim or the caster dies. +spell.wizardry:darkness_orb.desc=Fires a slow moving bolt of dark energy in the direction you are pointing, which withers whatever it hits. +spell.wizardry:darkvision.desc=Grants the caster night vision for 45 seconds. +spell.wizardry:dart.desc=Fires a dart in the direction you are pointing which damages and weakens its target. +spell.wizardry:decay.desc=Creates a patch of decay on the ground which infects any creature that touches it, causing lingering damage over time and spreading more decay wherever it walks. +spell.wizardry:decoy.desc=Creates an illusory clone of the caster which tricks mobs into attacking it instead. The decoy will vanish after 30 seconds. +spell.wizardry:detonate.desc=Causes an explosion where you are pointing, damaging all nearby creatures - including the caster, if they are too close. +spell.wizardry:diamondflesh.desc="Your arrows are no match for me!" +spell.wizardry:earthquake.desc=A true master of earth magic can move mountains. +spell.wizardry:entrapment.desc=Traps the target in a sphere of darkness which pulls it helplessly upwards and continually damages it. +spell.wizardry:fireball.desc=Launches a fireball in the direction you are pointing. +spell.wizardry:firebolt.desc=Shoots a jet of fire a short distance in front of you. +spell.wizardry:firebomb.desc=Lanches a firebomb in the direction you are pointing which explodes on impact, setting targets on fire. +spell.wizardry:fire_resistance.desc=Grants the caster fire resistance for 30 seconds. +spell.wizardry:fire_sigil.desc=Places a magical fire trap on the ground which damages and sets on fire the creature that triggers it. +spell.wizardry:fireskin.desc=Cloaks the caster in flames for 30 seconds, causing anything that attacks them to catch fire. +spell.wizardry:firestorm.desc="I am the dragon." +spell.wizardry:flame_ray.desc=Creates a stream of flames in the direction you are pointing which sets on fire and continually damages targets. +spell.wizardry:flaming_axe.desc=Creates a flaming axe which sets enemies on fire when hit. Lasts for 30 seconds. +spell.wizardry:flaming_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with the power of flame, causing it to set fire to its victims. The magic wears off after 45 seconds. +spell.wizardry:flight.desc=Soar like an eagle. +spell.wizardry:font_of_mana.desc="We were filled with an intense magical energy appearing to emanate from the centre of the..." - Extract from the journal of a forgotten mage; the rest of the page has been burnt away. +spell.wizardry:font_of_vitality.desc=It feels amazing. +spell.wizardry:force_arrow.desc=Shoots an arrow of force in the direction you are pointing. +spell.wizardry:forcefield.desc=Creates a forcefield around the caster which repels creatures and deflects projectiles. +spell.wizardry:force_orb.desc=Launches a sphere of force which damages and repels nearby creatures on impact. +spell.wizardry:forests_curse.desc="How dare you enter my forest!" +spell.wizardry:freeze.desc=Freezes the target for 10 seconds. Will also freeze water and create snow on the ground. +spell.wizardry:freezing_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with the power of frost, causing it to freeze its victims. The magic wears off after 45 seconds. +spell.wizardry:frost_axe.desc=Creates a frozen axe which freezes enemies when hit. Lasts for 30 seconds. +spell.wizardry:frost_ray.desc=Creates a stream of frost in the direction you are pointing which slows and continually damages targets. +spell.wizardry:frost_sigil.desc=Places a magical ice trap on the ground which damages and freezes the creature that triggers it. +spell.wizardry:glide.desc=Allows the caster to glide downwards while in the air and holding the use item button. +spell.wizardry:greater_fireball.desc=Launches a large fireball in the direction you are pointing which explodes on impact. +spell.wizardry:greater_heal.desc=Heals the caster by 4 hearts. +spell.wizardry:group_heal.desc=Heals the caster and all nearby allies and summoned creatures by 3 hearts. +spell.wizardry:growth_aura.desc=Grows all crops near the caster. Also grows tall grass and flowers on grass. +spell.wizardry:hailstorm.desc=It was during the great winter of the third age that the ice mages discovered their true power. +spell.wizardry:heal.desc=Heals the caster by 2 hearts. +spell.wizardry:heal_ally.desc=Heals the target by 2 and a half hearts. +spell.wizardry:healing_aura.desc=Creates a zone of healing energy which regenerates the health of any ally inside it. Any undead inside the healing aura will slowly take damage. +spell.wizardry:homing_spark.desc=Creates a floating spark which moves towards enemies. +spell.wizardry:ice_age.desc="You shall be frozen for an eternity!" +spell.wizardry:ice_charge.desc=Launches an ice charge which explodes on impact, freezing nearby creatures and releasing shards in all directions. +spell.wizardry:ice_lance.desc=Fires a great spear of ice in the direction you are pointing which overpenetrates targets, damaging and freezing them in the process. +spell.wizardry:ice_shard.desc=Fires a shard of ice in the direction you are pointing which damages and slows targets when hit. +spell.wizardry:ice_shroud.desc=Creates a shroud of cold around the caster for 30 seconds, causing anything that attacks them to be frozen. +spell.wizardry:ice_spikes.desc=Causes razor-sharp ice spikes to rise from the ground where you are pointing, skewering any creatures caught amongst them. +spell.wizardry:ice_statue.desc=Freezes the target solid for 20 seconds or until broken out. The target cannot move or do anything while frozen but is also impervious to all damage. +spell.wizardry:ignite.desc=Sets the target on fire for 10 seconds. Also works like a flint and steel. +spell.wizardry:imbue_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with magic, rendering it more effective. The magic wears off after 45 seconds. +spell.wizardry:intimidate.desc=Emits an intimidating growl which causes nearby creatures to run away in fear. Fear stricken creatures will recover after 30 seconds. +spell.wizardry:invigorating_presence.desc=Grants the caster and all nearby allies increased strength for 45 seconds. +spell.wizardry:invisibility.desc=Makes the caster invisible for 30 seconds. +spell.wizardry:invoke_weather.desc=Changes the weather in the world. +spell.wizardry:ironflesh.desc=Greatly improves the caster's damage resistance for 30 seconds. +spell.wizardry:leap.desc=Causes the caster to jump upwards several blocks and slightly forward. +spell.wizardry:levitation.desc=Raises the caster upwards while the use item button is held. Will also negate fall damage if used before hitting the ground. +spell.wizardry:life_drain.desc=Creates a stream of withering energy in the direction you are pointing which drains the life of the target and uses it to gradually regenerate your health. +spell.wizardry:light.desc=Creates a magical point of light which illuminates the surrounding area. Lasts for 30 seconds. +spell.wizardry:lightning_arrow.desc=Shoots an arrow of lightning in the direction you are pointing. +spell.wizardry:lightning_bolt.desc=Causes lightning to strike where you are pointing. +spell.wizardry:lightning_disc.desc=Sends a disc of lightning flying off in the direction you are pointing, which seeks targets. +spell.wizardry:lightning_hammer.desc="I smite you by the wrath of the heavens!" +spell.wizardry:lightning_pulse.desc=Charges the ground around the caster with lightning, damaging and repelling nearby creatures. +spell.wizardry:lightning_ray.desc=Creates a stream of lightning in the direction you are pointing which continually damages targets. +spell.wizardry:lightning_sigil.desc=Places a magical lightning trap on the ground which damages the creature that triggers it and chains lightning to other nearby creatures. +spell.wizardry:lightning_web.desc="Focus. Channel the storm in your mind through your wand and unleash its fury." +spell.wizardry:magic_missile.desc=Fires a bolt of magical energy in the direction you are pointing. +spell.wizardry:metamorphosis.desc=Changes the target into another form. Only works on some creatures. +spell.wizardry:meteor.desc=Some wizards just want to see the world burn... +spell.wizardry:mind_control.desc=Takes control of the target's mind for 30 seconds, causing it switch sides and fight for the caster instead. Will not work on creatures that are too strong-willed. +spell.wizardry:mind_trick.desc=Confuses and disorients the target for 15 seconds, rendering it unable to attack effectively. The effect will be dispelled if the target takes damage. +spell.wizardry:none.desc=To get a spell book with the /give command, use metadata: /give [player] wizardry:spell_book 1 [spell id] (if you found this book in a chest, some other mod has messed things up). +spell.wizardry:oakflesh.desc=Improves the caster's damage resistance for 30 seconds. +spell.wizardry:petrify.desc=Turns the target to stone until broken out, with a chance for it to break out when it is dark. The target cannot move or do anything while petrified but is also impervious to all damage. +spell.wizardry:phase_step.desc=Teleports the caster through a 1 block thick wall in front of them. Range upgrades will increase the thickness you can teleport through. +spell.wizardry:plague_of_darkness.desc=The darkness will consume them all... +spell.wizardry:pocket_furnace.desc=Smelts up to 5 smeltable items in the caster's inventory. Items on the hotbar will be smelted first. +spell.wizardry:pocket_workbench.desc=Allows the caster to craft items as if they were at a crafting table. +spell.wizardry:poison.desc=Fires poison in the direction you are pointing. +spell.wizardry:poison_bomb.desc=Lanches a poison bomb in the direction you are pointing which explodes on impact, poisoning nearby creatures. +spell.wizardry:replenish_hunger.desc=Replenishes the caster's food level by 6 hunger points. +spell.wizardry:ring_of_fire.desc=Creates a ring of fire around the caster, damaging all nearby enemies and setting them on fire. +spell.wizardry:shadow_ward.desc=Creates a wall of darkness in front of the caster which causes half of all incoming damage to be inflicted upon the attacker instead. +spell.wizardry:shield.desc=Creates a protective barrier of force that blocks projectiles and magic. Also grants the caster a weak resistance effect. +spell.wizardry:shockwave.desc=Boom. +spell.wizardry:silverfish_swarm.desc="Ahhhh! They're MULTIPLYING!" +spell.wizardry:sixth_sense.desc=Allows the caster to sense the locations of nearby creatures, even through walls, for 20 seconds. +spell.wizardry:slime.desc=Engulfs the target in slime which slows and continually damages it. The slime bursts after 10 seconds. +spell.wizardry:smoke_bomb.desc=Launches a smoke bomb in the direction you are pointing which explodes on impact, releasing smoke and blinding nearby creatures for a short time. +spell.wizardry:snare.desc=Sets a trap on the ground which damages and briefly slows the creature that triggers it. +spell.wizardry:snowball.desc=Launches a snowball in the direction you are pointing. +spell.wizardry:spark_bomb.desc=Launches a shock charge in the direction you are pointing which releases sparks at nearby enemies on impact. +spell.wizardry:spectral_pathway.desc=Creates an indestructible magical bridge in front of you which extends for 15 blocks. The bridge vanishes after 60 seconds. +spell.wizardry:spider_swarm.desc=Summons a swarm of venomous spiders to fight for you. The spiders will disappear after 30 seconds or if they are killed. +spell.wizardry:static_aura.desc=Surrounds the caster with lightning for 30 seconds, firing a spark of lightning at anything that hits them. +spell.wizardry:summon_blaze.desc=Summons a blaze to fight for you. The blaze will disappear after 30 seconds or if it is killed. +spell.wizardry:summon_ice_giant.desc="Smash them!" +spell.wizardry:summon_ice_wraith.desc=Summons an ice wraith to fight for you. The ice wraith will disappear after 30 seconds or if it is killed. +spell.wizardry:summon_iron_golem.desc=Automatic automated autonomous automaton. +spell.wizardry:summon_lightning_wraith.desc=Summons a lightning wraith to fight for you. The lightning wraith will disappear after 30 seconds or if it is killed. +spell.wizardry:summon_phoenix.desc=From the ashes... +spell.wizardry:summon_shadow_wraith.desc=Summons a shadow wraith to fight for you. +spell.wizardry:summon_skeleton.desc=Summons a skeleton to fight for you. The skeleton will disappear after 30 seconds or if it is killed. +spell.wizardry:summon_skeleton_legion.desc="Rise, undead army!" +spell.wizardry:summon_snow_golem.desc=Creates a snow golem to fight for you. Lasts until the snow golem dies. +spell.wizardry:summon_spirit_horse.desc=Summons a spirit horse for you to ride. The spirit horse will vanish a short while after it is dismounted, or you can dismiss it by shift-right-clicking on it with any wand. +spell.wizardry:summon_spirit_wolf.desc=Summons a spirit wolf companion to fight for you. The spirit wolf will only disappear if it is killed, or you can dismiss it by shift-right-clicking on it with any wand. +spell.wizardry:summon_storm_elemental.desc="Storm Elemental: An ancient manifestation of the elements, it can hardly contain the raw power churning within it." - The Wizard's Guide to Arcane Beings, Volume I_i +spell.wizardry:summon_wither_skeleton.desc=Summons a wither skeleton to fight for you. The wither skeleton will disappear after 30 seconds or if it is killed. +spell.wizardry:summon_zombie.desc=Summons a zombie to fight for you. The zombie will disappear after 30 seconds or if it is killed. +spell.wizardry:telekinesis.desc=Moves an item or other small object towards you, or right-clicks the block you are looking at. Can also be used to disarm players. +spell.wizardry:thunderbolt.desc=Shoots a bolt of thunder which knocks back targets. +spell.wizardry:thunderstorm.desc="Mwahahahahahaha!" +spell.wizardry:tornado.desc=Unleashes a tornado in the direction you are pointing which hurls anything in its path skywards. +spell.wizardry:transience.desc=Makes the caster transient for 20 seconds. The caster is immune to all damage while transient but cannot break or place blocks or cause any damage. +spell.wizardry:transportation.desc=Transports the caster to their remembered stone circle. To use this spell, make a circle of stones of transportation, then right click it with a wand. +spell.wizardry:vanishing_box.desc=Grants the caster access to their ender chest storage. +spell.wizardry:wall_of_frost.desc=Winter at your fingertips. +spell.wizardry:water_breathing.desc=Allows the caster to breathe underwater for 60 seconds. +spell.wizardry:whirlwind.desc=Causes the target to be blown upwards and away from you at speed. +spell.wizardry:wither.desc=Fires a ray of darkness which withers anything it touches. +spell.wizardry:wither_skull.desc=Launches a wither skull in the direction you are pointing. + +spell.wizardry:invoke_weather.sun=The rain begins to stop... +spell.wizardry:invoke_weather.rain=The heavens open... +spell.wizardry:transportation.missing=Your remembered stone circle is missing or obstructed... +spell.wizardry:transportation.undefined=You must remember the location of a stone circle first! +spell.wizardry:transportation.wrongdimension=Your remembered stone circle is in another dimension... +spell.wizardry:clairvoyance.searching=Searching... +spell.wizardry:clairvoyance.confirm=The path revealed upon casting %1$s will now lead back to this point +spell.wizardry:clairvoyance.outofrange=Your remembered location is too far away or inaccessible... +spell.wizardry:clairvoyance.undefined=You must remember a location first! +spell.wizardry:clairvoyance.wrongdimension=Your remembered location is in another dimension... + +potion.wizardry:frost=Frostbite +potion.wizardry:fireskin=Fireskin +potion.wizardry:ice_shroud=Ice Shroud +potion.wizardry:static_aura=Static Aura +potion.wizardry:transience=Transience +potion.wizardry:decay=Decay +potion.wizardry:sixth_sense=Sixth Sense +potion.wizardry:arcane_jammer=Arcane Jammer +potion.wizardry:mind_trick=Mind Trick +potion.wizardry:mind_control=Mind Control +potion.wizardry:font_of_mana=Font of Mana +potion.wizardry:fear=Fear + +enchantment.wizardry:magic_sword=Imbuement +enchantment.wizardry:magic_bow=Imbuement +enchantment.wizardry:flaming_weapon=Fire Imbuement +enchantment.wizardry:freezing_weapon=Frost Imbuement + +key.categories.wizardry=Wizardry + +key.wizardry:next_spell=Next Spell +key.wizardry:previous_spell=Previous Spell + +death.attack.wizardry_magic=%1$s was killed by %2$s using magic +death.attack.indirect_wizardry_magic=%1$s was killed by %2$s using magic + +commands.wizardry:cast.usage=/%1$s [player] [damage multiplier] [range multiplier] [duration multiplier] [blast multiplier] +commands.wizardry:cast.success=Successfully cast %1$s +commands.wizardry:cast.success_continuous=Successfully cast %1$s; repeat the command to stop +commands.wizardry:cast.success_remote=Successfully cast %1$s as %2$s +commands.wizardry:cast.success_remote_continuous=Successfully cast %1$s as %2$s; repeat the command to stop +commands.wizardry:cast.fail=Unable to cast %1$s +commands.wizardry:cast.not_found=There is no such spell with ID %1$s +commands.wizardry:cast.tag_error=Data tag parsing failed: %s + +commands.wizardry:ally.usage=/%1$s [player] +commands.wizardry:ally.addally=%1$s has been added to %2$s's list of allies +commands.wizardry:ally.removeally=%1$s has been removed from %2$s's list of allies +commands.wizardry:ally.self=Players cannot be an ally of themselves! +commands.wizardry:ally.permission=You do not have permission to change other players' allies + +commands.wizardry:allies.usage=/%1$s [player] +commands.wizardry:allies.list=Players allied to you: %1$s +commands.wizardry:allies.list_other=Players allied to %1$s: %2$s +commands.wizardry:allies.permission=You do not have permission to view other players' allies +commands.wizardry:allies.none=None + +commands.wizardry:discoverspell.usage=/%1$s [player] +commands.wizardry:discoverspell.not_found=There is no such spell with ID %1$s +commands.wizardry:discoverspell.clear=Cleared all spell discovery data for %1$s +commands.wizardry:discoverspell.all=Added all spells to %1$s's spell discovery data +commands.wizardry:discoverspell.addspell=Added %1$s to %2$s's spell discovery data +commands.wizardry:discoverspell.removespell=Removed %1$s from %2$s's spell discovery data + +config.wizardry.title.general=Mod Options + +config.wizardry.category.spells=Configure Spells +config.wizardry.category.spells.tooltip=Select which spells are enabled +config.wizardry.title.spells=Spell Configuration +config.wizardry.subtitle.spells=Set a spell to false to disable it. + +config.wizardry.category.resistances=Configure Resistances +config.wizardry.category.resistances.tooltip=Configure which mobs are immune to different types of magic +config.wizardry.title.resistances=Resistance Configuration +config.wizardry.subtitle.resistances=See descriptions of individual options for more details. + +config.wizardry.category.ids=Configure IDs +config.wizardry.category.ids.tooltip=Change the IDs used by wizardry +config.wizardry.title.ids=ID Configuration +config.wizardry.subtitle.ids=Change these IDs if they conflict with another mod. + +config.wizardry.tower_rarity=Tower Rarity +config.wizardry.ore_dimensions=Ore Dimensions +config.wizardry.flower_dimensions=Flower Dimensions +config.wizardry.tower_dimensions=Tower Dimensions +config.wizardry.spell_book_drop_chance=Spell Book Drop Chance +config.wizardry.generate_loot=Generate Loot +config.wizardry.firebomb_is_craftable=Firebomb Is Craftable +config.wizardry.poison_bomb_is_craftable=Poison Bomb Is Craftable +config.wizardry.smoke_bomb_is_craftable=Smoke Bomb Is Craftable +config.wizardry.use_alternate_scroll_recipe=Use Alternate Scroll Recipe +config.wizardry.teleport_through_unbreakable_blocks=Teleport Through Unbreakable Blocks +config.wizardry.show_summoned_creature_names=Show Summoned Creature Names +config.wizardry.friendly_fire=Friendly Fire +config.wizardry.telekinetic_disarmament=Telekinetic Disarmament +config.wizardry.discovery_mode=Discovery Mode +config.wizardry.enable_shift_scrolling=Enable Shift-_scrolling +config.wizardry.minion_revenge_targeting=Minion Revenge Targeting +config.wizardry.player_damage_scaling=Player Damage Scaling Factor +config.wizardry.npc_damage_scaling=NPC Damage Scaling Factor +config.wizardry.cast_command_multiplier_limit=Cast Command Multiplier Limit +config.wizardry.summoned_creature_targets_whitelist=Summoned Creature Target Whitelist +config.wizardry.summoned_creature_targets_blacklist=Summoned Creature Target Blacklist +config.wizardry.spell_hud_position=Spell HUD Position +config.wizardry.cast_command_name=Cast Spell Command Name +config.wizardry.discoverspell_command_name=Discover Spell Command Name +config.wizardry.ally_command_name=Set Ally Command Name +config.wizardry.allies_command_name=View Allies Command Name + +config.wizardry.mobs_immune_to_fire=Mobs Immune To Fire +config.wizardry.mobs_immune_to_ice=Mobs Immune To Ice +config.wizardry.mobs_immune_to_lightning=Mobs Immune To Lightning +config.wizardry.mobs_immune_to_wither=Mobs Immune To Wither +config.wizardry.mobs_immune_to_poison=Mobs Immune To Poison + +config.wizardry.tower_rarity.tooltip=Rarity of wizard towers. Higher numbers are rarer. Set to 0 to disable wizard towers completely. +config.wizardry.ore_dimensions.tooltip=List of dimension ids in which crystal ore will generate. Note that removing the overworld (id 0) from this list will make the mod VERY difficult to play! +config.wizardry.flower_dimensions.tooltip=List of dimension ids in which crystal flowers will generate. +config.wizardry.tower_dimensions.tooltip=List of dimension ids in which wizard towers will generate. +config.wizardry.spell_book_drop_chance.tooltip=The chance for mobs to drop a spell book when killed. The greater this number, the more often they will drop. Set to 0 to disable spell book drops. Set to 200 for guaranteed drops. +config.wizardry.generate_loot.tooltip=Whether to generate wizardry loot in dungeon chests. +config.wizardry.firebomb_is_craftable.tooltip=Whether firebombs can be crafted or not. +config.wizardry.poison_bomb_is_craftable.tooltip=Whether poison bombs can be crafted or not. +config.wizardry.smoke_bomb_is_craftable.tooltip=Whether smoke bombs can be crafted or not. +config.wizardry.use_alternate_scroll_recipe.tooltip=Whether to require a magic crystal in the shapeless crafting recipe for blank scrolls. Set to true if another mod adds a conflicting recipe. +config.wizardry.teleport_through_unbreakable_blocks.tooltip=Whether players are allowed to teleport through unbreakable blocks (e.g. bedrock) using the phase step spell. +config.wizardry.show_summoned_creature_names.tooltip=Whether to show summoned creatures' names and owners above their heads. +config.wizardry.friendly_fire.tooltip=Whether to allow players to damage their designated allies using magic. +config.wizardry.telekinetic_disarmament.tooltip=Whether to allow players to disarm other players using the telekinesis spell.wizardry: Set to false to prevent stealing of items. +config.wizardry.discovery_mode.tooltip=For those who like a sense of mystery! When set to true, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is false. +config.wizardry.enable_shift_scrolling.tooltip=Whether you can switch between spells on a wand by scrolling with the mouse wheel while sneaking. Note that this will only affect you; other players connected to the same server obey their own settings. +config.wizardry.minion_revenge_targeting.tooltip=Whether summoned creatures can revenge attack their owner if their owner attacks them. +config.wizardry.player_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by players casting spells, relative to 1. +config.wizardry.npc_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1. +config.wizardry.cast_command_multiplier_limit.tooltip=Upper limit for the multipliers passed into the /cast command. This is here to stop players from accidentally breaking a world/server. Large blast mutipliers can cause extreme lag - you have been warned! +config.wizardry.summoned_creature_targets_whitelist.tooltip=List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). +config.wizardry.summoned_creature_targets_blacklist.tooltip=List of names of entities which summoned creatures and wizards are specifically not allowed to attack, overriding the defaults and the whitelist. Add creatures to this list if allowing them to be attacked causes problems or is too destructive (removing creepers from this list is done at your own risk!). Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). +config.wizardry.spell_hud_position.tooltip=The position of the spell HUD. +config.wizardry.cast_command_name.tooltip=The name of the /cast command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /cast you would type /magic instead. +config.wizardry.discoverspell_command_name.tooltip=The name of the /discoverspell command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /discoverspell you would type /magic instead. +config.wizardry.ally_command_name.tooltip=The name of the /ally command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /ally you would type /magic instead. +config.wizardry.allies_command_name.tooltip=The name of the /allies command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /allies you would type /magic instead. + +config.wizardry.mobs_immune_to_fire.tooltip=List of names of entities that are immune to fire, in addition to the defaults. Add mod creatures to this list if you want them to be immune to fire magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). +config.wizardry.mobs_immune_to_ice.tooltip=List of names of entities that are immune to ice, in addition to the defaults. Add mod creatures to this list if you want them to be immune to ice magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). +config.wizardry.mobs_immune_to_lightning.tooltip=List of names of entities that are immune to lightning, in addition to the defaults. Add mod creatures to this list if you want them to be immune to lightning magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). +config.wizardry.mobs_immune_to_wither.tooltip=List of names of entities that are immune to wither effects, in addition to the defaults. Add mod creatures to this list if you want them to be immune to withering magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). +config.wizardry.mobs_immune_to_poison.tooltip=List of names of entities that are immune to poison, in addition to the defaults. Add mod creatures to this list if you want them to be immune to poison magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). + +wizard.debug=%1$s, %2$s, %3$s \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/lang/en_US.lang b/src/main/resources/assets/wizardry/lang/en_US.lang new file mode 100644 index 00000000..3f87afb7 --- /dev/null +++ b/src/main/resources/assets/wizardry/lang/en_US.lang @@ -0,0 +1,738 @@ +tile.wizardry:arcane_workbench.name=Arcane Workbench +tile.wizardry:crystal_ore.name=Crystal Ore +tile.wizardry:petrified_stone.name=Petrified Stone +tile.wizardry:ice_statue.name=Ice Statue +tile.wizardry:crystal_flower.name=Crystal Flower +tile.wizardry:snare.name=Snare +tile.wizardry:transportation_stone.name=Stone of Transportation +tile.wizardry:spectral_block.name=Spectral Block +tile.wizardry:crystal_block.name=Block of Crystal + +item.wizardry:magic_crystal.name=Magic Crystal +item.wizardry:magic_wand.name=Magic Wand +item.wizardry:apprentice_wand.name=Apprentice Wand +item.wizardry:advanced_wand.name=Advanced Wand +item.wizardry:master_wand.name=Master Wand +item.wizardry:spell_book.name=Spell Book + +item.wizardry:arcane_tome.name=Tome of Arcana +item.wizardry:arcane_tome.desc1=Upgrades any %1$s +item.wizardry:arcane_tome.desc2=wand to %1$s tier + +item.wizardry:wizard_handbook.name=The Wizard's Handbook +item.wizardry:wizard_handbook.desc=by %1$s + +item.wizardry:wand.buff=+%1$s %2$s potency +item.wizardry:wand.spell=Current Spell: %1$s +item.wizardry:wand.mana=Mana: %1$s/%2$s + +item.wizardry:wand.addally=%1$s has been added to your list of allies +item.wizardry:wand.removeally=%1$s has been removed from your list of allies + +item.wizardry:basic_fire_wand.name=Wand of Embers +item.wizardry:basic_ice_wand.name=Wand of Frost +item.wizardry:basic_lightning_wand.name=Wand of Sparks +item.wizardry:basic_necromancy_wand.name=Wand of Shadows +item.wizardry:basic_earth_wand.name=Wand of the Forest +item.wizardry:basic_sorcery_wand.name=Wand of Mystery +item.wizardry:basic_healing_wand.name=Wand of Healing + +item.wizardry:apprentice_fire_wand.name=Apprentice Pyromancer Wand +item.wizardry:apprentice_ice_wand.name=Apprentice Ice Mage Wand +item.wizardry:apprentice_lightning_wand.name=Apprentice Storm Mage Wand +item.wizardry:apprentice_necromancy_wand.name=Apprentice Necromancer Wand +item.wizardry:apprentice_earth_wand.name=Apprentice Earth Mage Wand +item.wizardry:apprentice_sorcery_wand.name=Apprentice Sorcerer Wand +item.wizardry:apprentice_healing_wand.name=Apprentice Healer Wand + +item.wizardry:advanced_fire_wand.name=Wand of the Pyromancer +item.wizardry:advanced_ice_wand.name=Wand of the Ice Mage +item.wizardry:advanced_lightning_wand.name=Wand of the Storm Mage +item.wizardry:advanced_necromancy_wand.name=Wand of the Necromancer +item.wizardry:advanced_earth_wand.name=Wand of the Earth Mage +item.wizardry:advanced_sorcery_wand.name=Wand of the Sorcerer +item.wizardry:advanced_healing_wand.name=Wand of the Healer + +item.wizardry:master_fire_wand.name=Master Pyromancer Wand +item.wizardry:master_ice_wand.name=Master Ice Mage Wand +item.wizardry:master_lightning_wand.name=Master Storm Mage Wand +item.wizardry:master_necromancy_wand.name=Master Necromancer Wand +item.wizardry:master_earth_wand.name=Master Earth Mage Wand +item.wizardry:master_sorcery_wand.name=Master Sorcerer Wand +item.wizardry:master_healing_wand.name=Master Healer Wand + +item.wizardry:spectral_sword.name=Spectral Sword +item.wizardry:spectral_pickaxe.name=Spectral Pickaxe +item.wizardry:spectral_bow.name=Spectral Bow + +item.wizardry:mana_flask.name=Mana Flask +item.wizardry:storage_upgrade.name=Wand Storage Upgrade +item.wizardry:siphon_upgrade.name=Wand Siphon Upgrade +item.wizardry:condenser_upgrade.name=Wand Condenser Upgrade +item.wizardry:range_upgrade.name=Wand Range Upgrade +item.wizardry:duration_upgrade.name=Wand Duration Upgrade +item.wizardry:cooldown_upgrade.name=Wand Cooldown Upgrade +item.wizardry:blast_upgrade.name=Wand Blast Upgrade +item.wizardry:attunement_upgrade.name=Wand Attunement Upgrade + +item.wizardry:flaming_axe.name=Flaming Axe +item.wizardry:frost_axe.name=Frost Axe + +item.wizardry:firebomb.name=Firebomb +item.wizardry:poison_bomb.name=Poison Bomb +item.wizardry:smoke_bomb.name=Smoke Bomb + +item.wizardry:blank_scroll.name=Blank Scroll +item.wizardry:scroll.name=Scroll of %1$s +item.wizardry:scroll.undiscovered.name=Scroll "%1$s" +item.wizardry:identification_scroll.name=Scroll of Identification +item.wizardry:identification_scroll.desc1=%1$sIdentifies an unknown +item.wizardry:identification_scroll.desc2=%1$sspell book or scroll +item.wizardry:identification_scroll.nothing_to_identify=Nothing to identify! + +item.wizardry:armour_upgrade.name=Arcane Seal of Protection +item.wizardry:armour_upgrade.desc1=%1$sUpgrades any wizard armour +item.wizardry:armour_upgrade.desc2=%1$sto make it %2$slegendary + +item.wizardry:magic_silk.name=Magical Silk + +item.wizardry:wizard_armour.legendary=Legendary +item.wizardry:wizard_armour.buff=-%1$s %2$s cost +item.wizardry:wizard_armour.mana=Mana: %1$s/%2$s + +item.wizardry:wizard_hat.name=Wizard Hat +item.wizardry:wizard_robe.name=Wizard Robes +item.wizardry:wizard_leggings.name=Wizard Leggings +item.wizardry:wizard_boots.name=Wizard Boots + +item.wizardry:wizard_hat_fire.name=Pyromancer Hat +item.wizardry:wizard_robe_fire.name=Pyromancer Robes +item.wizardry:wizard_leggings_fire.name=Pyromancer Leggings +item.wizardry:wizard_boots_fire.name=Pyromancer Boots + +item.wizardry:wizard_hat_ice.name=Ice Mage Hat +item.wizardry:wizard_robe_ice.name=Ice Mage Robes +item.wizardry:wizard_leggings_ice.name=Ice Mage Leggings +item.wizardry:wizard_boots_ice.name=Ice Mage Boots + +item.wizardry:wizard_hat_lightning.name=Storm Mage Hat +item.wizardry:wizard_robe_lightning.name=Storm Mage Robes +item.wizardry:wizard_leggings_lightning.name=Storm Mage Leggings +item.wizardry:wizard_boots_lightning.name=Storm Mage Boots + +item.wizardry:wizard_hat_necromancy.name=Necromancer Hat +item.wizardry:wizard_robe_necromancy.name=Necromancer Robes +item.wizardry:wizard_leggings_necromancy.name=Necromancer Leggings +item.wizardry:wizard_boots_necromancy.name=Necromancer Boots + +item.wizardry:wizard_hat_earth.name=Earth Mage Hat +item.wizardry:wizard_robe_earth.name=Earth Mage Robes +item.wizardry:wizard_leggings_earth.name=Earth Mage Leggings +item.wizardry:wizard_boots_earth.name=Earth Mage Boots + +item.wizardry:wizard_hat_sorcery.name=Sorcerer Hat +item.wizardry:wizard_robe_sorcery.name=Sorcerer Robes +item.wizardry:wizard_leggings_sorcery.name=Sorcerer Leggings +item.wizardry:wizard_boots_sorcery.name=Sorcerer Boots + +item.wizardry:wizard_hat_healing.name=Healer Hat +item.wizardry:wizard_robe_healing.name=Healer Robes +item.wizardry:wizard_leggings_healing.name=Healer Leggings +item.wizardry:wizard_boots_healing.name=Healer Boots + +item.wizardry:spawn_wizard.name=Spawn Wizard +item.wizardry:spawn_evil_wizard.name=Spawn Evil Wizard + +item.wizardry:spectral_helmet.name=Spectral Helmet +item.wizardry:spectral_chestplate.name=Spectral Chestplate +item.wizardry:spectral_leggings.name=Spectral Leggings +item.wizardry:spectral_boots.name=Spectral Boots + +entity.wizardry.summonedcreature.nameplate=%1$s's %2$s +entity.wizardry.summonedcreature.nameplate_fallback=Someone's %1$s + +entity.wizardry.zombie_minion.name=Zombie +entity.wizardry.skeleton_minion.name=Skeleton +entity.wizardry.spider_minion.name=Spider +entity.wizardry.blaze_minion.name=Blaze +entity.wizardry.ice_wraith.name=Ice Wraith +entity.wizardry.lightning_wraith.name=Lightning Wraith +entity.wizardry.shadow_wraith.name=Shadow Wraith +entity.wizardry.spirit_wolf.name=Spirit Wolf +entity.wizardry.spirit_horse.name=Spirit Horse +entity.wizardry.ice_giant.name=Ice Giant +entity.wizardry.phoenix.name=Phoenix +entity.wizardry.wizard.name=Wizard +entity.wizardry.magic_slime.name=Magical Slime +entity.wizardry.silverfish_minion.name=Silverfish +entity.wizardry.storm_elemental.name=Storm Elemental +entity.wizardry.evil_wizard.name=Wizard +entity.wizardry.decoy.name=Decoy + +entity.wizardry.magic_missile.name=Magic +entity.wizardry.arc.name=Magic +entity.wizardry.spark_bomb.name=Magic +entity.wizardry.ice_shard.name=Magic +entity.wizardry.firebomb.name=Magic +entity.wizardry.poison_bomb.name=Magic +entity.wizardry.force_orb.name=Magic +entity.wizardry.spark.name=Magic +entity.wizardry.darkness_orb.name=Magic +entity.wizardry.fire_sigil.name=Magic +entity.wizardry.frost_sigil.name=Magic +entity.wizardry.lightning_sigil.name=Magic +entity.wizardry.lightning_arrow.name=Magic +entity.wizardry.firebolt.name=Magic +entity.wizardry.ice_charge.name=Magic +entity.wizardry.force_arrow.name=Magic +entity.wizardry.dart.name=Magic +entity.wizardry.lightning_disc.name=Magic +entity.wizardry.thunderbolt.name=Magic +entity.wizardry.decay.name=Magic +entity.wizardry.ice_lance.name=Magic +entity.wizardry.smoke_bomb.name=Magic +entity.wizardry.ice_spike.name=Magic + +entity.wizardry.black_hole.name=Black Hole +entity.wizardry.shield.name=Shield +entity.wizardry.meteor.name=Meteor +entity.wizardry.blizzard.name=Blizzard +entity.wizardry.bubble.name=Bubble +entity.wizardry.tornado.name=Tornado +entity.wizardry.lightning_hammer.name=Lightning Hammer +entity.wizardry.arrow_rain.name=Arrow Rain +entity.wizardry.healing_aura.name=Healing Aura +entity.wizardry.forcefield.name=Forcefield +entity.wizardry.ring_of_fire.name=Ring of Fire +entity.wizardry.earthquake.name=Earthquake +entity.wizardry.falling_grass.name=Falling Grass +entity.wizardry.hailstorm.name=Hailstorm +entity.wizardry.lightning_pulse.name=Lightning Pulse + +itemGroup.wizardry=Wizardry +itemGroup.wizardryspells=Spells + +achievement.crystal=A Curious Crystal... +achievement.crystal.desc=Mine a magic crystal +achievement.arcane_initiate=Arcane Initiate +achievement.arcane_initiate.desc=Craft a magic wand with a gold nugget, a stick and a magic crystal +achievement.apprentice=Wizard's Apprentice +achievement.apprentice.desc=Use a tome of arcana to upgrade your wand +achievement.master=Arcane Master +achievement.master.desc=Obtain a master wand +achievement.all_spells=Mage of All Trades +achievement.all_spells.desc=Cast every single spell in the game +achievement.wizard_trade=Magic Dealing +achievement.wizard_trade.desc=Purchase an item from a wizard +achievement.buy_master_spell=Knowledge is Power +achievement.buy_master_spell.wizardry:desc=Purchase a master spell from a wizard +achievement.freeze_blaze=Not So Hot Now +achievement.freeze_blaze.desc=Freeze a blaze solid +achievement.charge_creeper=It's Gonna Blow +achievement.charge_creeper.desc='Accidentally' charge a creeper +achievement.frankenstein=Frankenstein +achievement.frankenstein.desc=Turn a pig into a zombie pigman using the lightning bolt spell +achievement.special_upgrade=Arcane Tinkering +achievement.special_upgrade.desc=Apply a special upgrade to a wand +achievement.craft_flask=It's Magic, Bottled! +achievement.craft_flask.desc=Craft a mana flask +achievement.elemental=Elemental +achievement.elemental.desc=Obtain an elemental wand +achievement.armour_set=Now You're a Proper Wizard +achievement.armour_set.desc=Craft and equip a full set of wizard armor +achievement.legendary=Legendary +achievement.legendary.desc=Obtain a piece of legendary wizard armor +achievement.self_destruct=That Backfired +achievement.self_destruct.desc=Get killed by your own magic +achievement.pig_tornado=Not Again... +achievement.pig_tornado.desc=Ride a pig into a tornado +achievement.jam_wizard=Jamming Session +achievement.jam_wizard.desc=Use the arcane jammer spell on a wizard +achievement.slime_skeleton=Sticky Situation +achievement.slime_skeleton.desc=Engulf a skeleton in slime +achievement.anger_wizard=You'll Regret That +achievement.anger_wizard.desc=Make a wizard angry +achievement.defeat_evil_wizard=Righteousness +achievement.defeat_evil_wizard.desc=Defeat an evil wizard +achievement.max_out_wand=Fully Equipped +achievement.max_out_wand.desc=Apply the maximum number of upgrades to a master wand +achievement.element_master=Element Mastery +achievement.element_master.desc=Cast all the spells of any element +achievement.identify_spell=Arcane Appraisal +achievement.identify_spell.wizardry:desc=Use a scroll of identification to identify a spell book or scroll + +tile.wizardry:transportation_stone.confirm=You will now be returned here upon casting %1$s +tile.wizardry:transportation_stone.invalid=You must make a circle with 8 stones of transportation first! + +container.wizardry:arcane_workbench=Arcane Workbench +container.wizardry:arcane_workbench.apply=Apply +container.wizardry:arcane_workbench.mana=Mana: +container.wizardry:arcane_workbench.upgrades=Applied Upgrades: + +tier.basic=Novice +tier.apprentice=Apprentice +tier.advanced=Advanced +tier.master=Master + +element.simple=None +element.fire=Fire +element.ice=Ice +element.lightning=Lightning +element.necromancy=Necromancy +element.earth=Earth +element.sorcery=Sorcery +element.healing=Healing + +element.simple.wizard=Wizard +element.fire.wizard=Pyromancer +element.ice.wizard=Ice Mage +element.lightning.wizard=Storm Mage +element.necromancy.wizard=Necromancer +element.earth.wizard=Earth Mage +element.sorcery.wizard=Sorcerer +element.healing.wizard=Healer + +spelltype.attack=Attack +spelltype.defence=Defense +spelltype.utility=Utility +spelltype.minion=Minion + +spell.disabled=%1$s has been disabled in the config +spell.resist=%1$s resisted %2$s +spell.discover=Discovered the spell %1$s! + +spell.wizardry:agility=Agility +spell.wizardry:arc=Arc +spell.wizardry:arcane_jammer=Arcane Jammer +spell.wizardry:arrow_rain=Arrow Rain +spell.wizardry:banish=Banish +spell.wizardry:black_hole=Black Hole +spell.wizardry:blink=Blink +spell.wizardry:blizzard=Blizzard +spell.wizardry:bubble=Bubble +spell.wizardry:chain_lightning=Chain Lightning +spell.wizardry:clairvoyance=Clairvoyance +spell.wizardry:cobwebs=Cobwebs +spell.wizardry:conjure_armour=Conjure Armor +spell.wizardry:conjure_bow=Conjure Bow +spell.wizardry:conjure_pickaxe=Conjure Pickaxe +spell.wizardry:conjure_sword=Conjure Sword +spell.wizardry:cure_effects=Cure Effects +spell.wizardry:curse_of_soulbinding=Curse of Soulbinding +spell.wizardry:darkness_orb=Darkness Orb +spell.wizardry:darkvision=Darkvision +spell.wizardry:dart=Dart +spell.wizardry:decay=Decay +spell.wizardry:decoy=Decoy +spell.wizardry:detonate=Detonate +spell.wizardry:diamondflesh=Diamondflesh +spell.wizardry:earthquake=Earthquake +spell.wizardry:entrapment=Entrapment +spell.wizardry:fireball=Fireball +spell.wizardry:firebolt=Firebolt +spell.wizardry:firebomb=Firebomb +spell.wizardry:fire_resistance=Fire Resistance +spell.wizardry:fire_sigil=Fire Sigil +spell.wizardry:fireskin=Fireskin +spell.wizardry:firestorm=Firestorm +spell.wizardry:flame_ray=Flame Ray +spell.wizardry:flaming_axe=Flaming Axe +spell.wizardry:flaming_weapon=Flaming Weapon +spell.wizardry:flight=Flight +spell.wizardry:font_of_mana=Font of Mana +spell.wizardry:font_of_vitality=Font of Vitality +spell.wizardry:force_arrow=Force Arrow +spell.wizardry:forcefield=Forcefield +spell.wizardry:force_orb=Force Orb +spell.wizardry:forests_curse=Forest's Curse +spell.wizardry:freeze=Freeze +spell.wizardry:freezing_weapon=Freezing Weapon +spell.wizardry:frost_axe=Frost Axe +spell.wizardry:frost_ray=Frost Ray +spell.wizardry:frost_sigil=Frost Sigil +spell.wizardry:glide=Glide +spell.wizardry:greater_fireball=Greater Fireball +spell.wizardry:greater_heal=Greater Heal +spell.wizardry:group_heal=Group Heal +spell.wizardry:growth_aura=Growth Aura +spell.wizardry:hailstorm=Hailstorm +spell.wizardry:heal=Heal +spell.wizardry:heal_ally=Heal Ally +spell.wizardry:healing_aura=Healing Aura +spell.wizardry:homing_spark=Homing Spark +spell.wizardry:ice_age=Ice Age +spell.wizardry:ice_charge=Ice Charge +spell.wizardry:ice_lance=Ice Lance +spell.wizardry:ice_shard=Ice Shard +spell.wizardry:ice_shroud=Ice Shroud +spell.wizardry:ice_spikes=Ice Spikes +spell.wizardry:ice_statue=Ice Statue +spell.wizardry:ignite=Ignite +spell.wizardry:imbue_weapon=Imbue Weapon +spell.wizardry:intimidate=Intimidate +spell.wizardry:invigorating_presence=Invigorating Presence +spell.wizardry:invisibility=Invisibility +spell.wizardry:invoke_weather=Invoke Weather +spell.wizardry:ironflesh=Ironflesh +spell.wizardry:leap=Leap +spell.wizardry:levitation=Levitation +spell.wizardry:life_drain=Life Drain +spell.wizardry:light=Light +spell.wizardry:lightning_arrow=Lightning Arrow +spell.wizardry:lightning_bolt=Lightning Bolt +spell.wizardry:lightning_disc=Lightning Disk +spell.wizardry:lightning_hammer=Lightning Hammer +spell.wizardry:lightning_pulse=Lightning Pulse +spell.wizardry:lightning_ray=Lightning Ray +spell.wizardry:lightning_sigil=Lightning Sigil +spell.wizardry:lightning_web=Lightning Web +spell.wizardry:magic_missile=Magic Missile +spell.wizardry:metamorphosis=Metamorphosis +spell.wizardry:meteor=Meteor +spell.wizardry:mind_control=Mind Control +spell.wizardry:mind_trick=Mind Trick +spell.wizardry:none=[Empty Slot] +spell.wizardry:oakflesh=Oakflesh +spell.wizardry:petrify=Petrify +spell.wizardry:phase_step=Phase Step +spell.wizardry:plague_of_darkness=Plague of Darkness +spell.wizardry:pocket_furnace=Pocket Furnace +spell.wizardry:pocket_workbench=Pocket Workbench +spell.wizardry:poison=Poison +spell.wizardry:poison_bomb=Poison Bomb +spell.wizardry:replenish_hunger=Replenish Hunger +spell.wizardry:ring_of_fire=Ring of Fire +spell.wizardry:shadow_ward=Shadow Ward +spell.wizardry:shield=Shield +spell.wizardry:shockwave=Shockwave +spell.wizardry:silverfish_swarm=Silverfish Swarm +spell.wizardry:sixth_sense=Sixth Sense +spell.wizardry:slime=Slime +spell.wizardry:smoke_bomb=Smoke Bomb +spell.wizardry:snare=Snare +spell.wizardry:snowball=Snowball +spell.wizardry:spark_bomb=Spark Bomb +spell.wizardry:spectral_pathway=Spectral Pathway +spell.wizardry:spider_swarm=Spider Swarm +spell.wizardry:static_aura=Static Aura +spell.wizardry:summon_blaze=Summon Blaze +spell.wizardry:summon_ice_giant=Summon Ice Giant +spell.wizardry:summon_ice_wraith=Summon Ice Wraith +spell.wizardry:summon_iron_golem=Summon Iron Golem +spell.wizardry:summon_lightning_wraith=Summon Lightning Wraith +spell.wizardry:summon_phoenix=Summon Phoenix +spell.wizardry:summon_shadow_wraith=Summon Shadow Wraith +spell.wizardry:summon_skeleton=Summon Skeleton +spell.wizardry:summon_skeleton_legion=Summon Skeleton Legion +spell.wizardry:summon_snow_golem=Summon Snow Golem +spell.wizardry:summon_spirit_horse=Summon Spirit Horse +spell.wizardry:summon_spirit_wolf=Summon Spirit Wolf +spell.wizardry:summon_storm_elemental=Summon Storm Elemental +spell.wizardry:summon_wither_skeleton=Summon Wither Skeleton +spell.wizardry:summon_zombie=Summon Zombie +spell.wizardry:telekinesis=Telekinesis +spell.wizardry:thunderbolt=Thunderbolt +spell.wizardry:thunderstorm=Thunderstorm +spell.wizardry:tornado=Tornado +spell.wizardry:transience=Transience +spell.wizardry:transportation=Transportation +spell.wizardry:vanishing_box=Vanishing Box +spell.wizardry:wall_of_frost=Wall of Frost +spell.wizardry:water_breathing=Water Breathing +spell.wizardry:whirlwind=Whirlwind +spell.wizardry:wither=Wither +spell.wizardry:wither_skull=Wither Skull + +spell.wizardry:agility.desc=Grants the caster faster movement speed and greater jump height for 30 seconds. +spell.wizardry:arc.desc=Fires a spark of lightning at the target. +spell.wizardry:arcane_jammer.desc=Prevents the target from using magic for 15 seconds. +spell.wizardry:arrow_rain.desc="Archers, fire!" +spell.wizardry:banish.desc=Teleports the target against its will to a random location within a certain range. +spell.wizardry:black_hole.desc=Tear reality asunder. +spell.wizardry:blink.desc=Teleports the caster over a short distance to where they are pointing. +spell.wizardry:blizzard.desc=Creates a zone of swirling icy wind which slows and continually damages anything trapped inside. The caster is immune to the damage but is still slowed. +spell.wizardry:bubble.desc=Fires a jet of bubbles which causes anything it hits to float upwards helplessly. The target will fall after a certain time or if it is damaged. +spell.wizardry:chain_lightning.desc=Fires a spark of lightning at the target, which then chains to additional targets up to twice. +spell.wizardry:clairvoyance.desc=Reveals the path to a remembered location. With this spell selected, sneak-right-click on a block to set the location. Cast this spell normally to reveal the path. The path will fade after 90 seconds. +spell.wizardry:cobwebs.desc=Creates cobwebs where you are pointing, which greatly hamper the movement of any creatures caught amongst them. The cobwebs will vanish after 20 seconds or if broken. +spell.wizardry:conjure_armour.desc=Creates spectral armor around the caster which offers protection equal to that of iron armor. The armor lasts for 60 seconds. The caster must have an empty armor slot. +spell.wizardry:conjure_bow.desc=Creates a spectral bow with unlimited arrows that lasts for 30 seconds. +spell.wizardry:conjure_pickaxe.desc=Creates a spectral pickaxe of equal strength to an iron pickaxe that lasts for 30 seconds. +spell.wizardry:conjure_sword.desc=Creates a spectral sword of equal strength to an iron sword that lasts for 30 seconds. +spell.wizardry:cure_effects.desc=Removes all potion effects currently affecting the caster, good or bad. +spell.wizardry:curse_of_soulbinding.desc=Causes the target's soul to be inextricably bound to that of the caster, meaning all damage dealt to the caster is also dealt to the victim. Lasts until either the victim or the caster dies. +spell.wizardry:darkness_orb.desc=Fires a slow moving bolt of dark energy in the direction you are pointing, which withers whatever it hits. +spell.wizardry:darkvision.desc=Grants the caster night vision for 45 seconds. +spell.wizardry:dart.desc=Fires a dart in the direction you are pointing which damages and weakens its target. +spell.wizardry:decay.desc=Creates a patch of decay on the ground which infects any creature that touches it, causing lingering damage over time and spreading more decay wherever it walks. +spell.wizardry:decoy.desc=Creates an illusory clone of the caster which tricks mobs into attacking it instead. The decoy will vanish after 30 seconds. +spell.wizardry:detonate.desc=Causes an explosion where you are pointing, damaging all nearby creatures - including the caster, if they are too close. +spell.wizardry:diamondflesh.desc="Your arrows are no match for me!" +spell.wizardry:earthquake.desc=A true master of earth magic can move mountains. +spell.wizardry:entrapment.desc=Traps the target in a sphere of darkness which pulls it helplessly upwards and continually damages it. +spell.wizardry:fireball.desc=Launches a fireball in the direction you are pointing. +spell.wizardry:firebolt.desc=Shoots a jet of fire a short distance in front of you. +spell.wizardry:firebomb.desc=Lanches a firebomb in the direction you are pointing which explodes on impact, setting targets on fire. +spell.wizardry:fire_resistance.desc=Grants the caster fire resistance for 30 seconds. +spell.wizardry:fire_sigil.desc=Places a magical fire trap on the ground which damages and sets on fire the creature that triggers it. +spell.wizardry:fireskin.desc=Cloaks the caster in flames for 30 seconds, causing anything that attacks them to catch fire. +spell.wizardry:firestorm.desc="I am the dragon." +spell.wizardry:flame_ray.desc=Creates a stream of flames in the direction you are pointing which sets on fire and continually damages targets. +spell.wizardry:flaming_axe.desc=Creates a flaming axe which sets enemies on fire when hit. Lasts for 30 seconds. +spell.wizardry:flaming_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with the power of flame, causing it to set fire to its victims. The magic wears off after 45 seconds. +spell.wizardry:flight.desc=Soar like an eagle. +spell.wizardry:font_of_mana.desc="We were filled with an intense magical energy appearing to emanate from the centre of the..." - Extract from the journal of a forgotten mage; the rest of the page has been burnt away. +spell.wizardry:font_of_vitality.desc=It feels amazing. +spell.wizardry:force_arrow.desc=Shoots an arrow of force in the direction you are pointing. +spell.wizardry:forcefield.desc=Creates a forcefield around the caster which repels creatures and deflects projectiles. +spell.wizardry:force_orb.desc=Launches a sphere of force which damages and repels nearby creatures on impact. +spell.wizardry:forests_curse.desc="How dare you enter my forest!" +spell.wizardry:freeze.desc=Freezes the target for 10 seconds. Will also freeze water and create snow on the ground. +spell.wizardry:freezing_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with the power of frost, causing it to freeze its victims. The magic wears off after 45 seconds. +spell.wizardry:frost_axe.desc=Creates a frozen axe which freezes enemies when hit. Lasts for 30 seconds. +spell.wizardry:frost_ray.desc=Creates a stream of frost in the direction you are pointing which slows and continually damages targets. +spell.wizardry:frost_sigil.desc=Places a magical ice trap on the ground which damages and freezes the creature that triggers it. +spell.wizardry:glide.desc=Allows the caster to glide downwards while in the air and holding the use item button. +spell.wizardry:greater_fireball.desc=Launches a large fireball in the direction you are pointing which explodes on impact. +spell.wizardry:greater_heal.desc=Heals the caster by 4 hearts. +spell.wizardry:group_heal.desc=Heals the caster and all nearby allies and summoned creatures by 3 hearts. +spell.wizardry:growth_aura.desc=Grows all crops near the caster. Also grows tall grass and flowers on grass. +spell.wizardry:hailstorm.desc=It was during the great winter of the third age that the ice mages discovered their true power. +spell.wizardry:heal.desc=Heals the caster by 2 hearts. +spell.wizardry:heal_ally.desc=Heals the target by 2 and a half hearts. +spell.wizardry:healing_aura.desc=Creates a zone of healing energy which regenerates the health of any ally inside it. Any undead inside the healing aura will slowly take damage. +spell.wizardry:homing_spark.desc=Creates a floating spark which moves towards enemies. +spell.wizardry:ice_age.desc="You shall be frozen for an eternity!" +spell.wizardry:ice_charge.desc=Launches an ice charge which explodes on impact, freezing nearby creatures and releasing shards in all directions. +spell.wizardry:ice_lance.desc=Fires a great spear of ice in the direction you are pointing which overpenetrates targets, damaging and freezing them in the process. +spell.wizardry:ice_shard.desc=Fires a shard of ice in the direction you are pointing which damages and slows targets when hit. +spell.wizardry:ice_shroud.desc=Creates a shroud of cold around the caster for 30 seconds, causing anything that attacks them to be frozen. +spell.wizardry:ice_spikes.desc=Causes razor-sharp ice spikes to rise from the ground where you are pointing, skewering any creatures caught amongst them. +spell.wizardry:ice_statue.desc=Freezes the target solid for 20 seconds or until broken out. The target cannot move or do anything while frozen but is also impervious to all damage. +spell.wizardry:ignite.desc=Sets the target on fire for 10 seconds. Also works like a flint and steel. +spell.wizardry:imbue_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with magic, rendering it more effective. The magic wears off after 45 seconds. +spell.wizardry:intimidate.desc=Emits an intimidating growl which causes nearby creatures to run away in fear. Fear stricken creatures will recover after 30 seconds. +spell.wizardry:invigorating_presence.desc=Grants the caster and all nearby allies increased strength for 45 seconds. +spell.wizardry:invisibility.desc=Makes the caster invisible for 30 seconds. +spell.wizardry:invoke_weather.desc=Changes the weather in the world. +spell.wizardry:ironflesh.desc=Greatly improves the caster's damage resistance for 30 seconds. +spell.wizardry:leap.desc=Causes the caster to jump upwards several blocks and slightly forward. +spell.wizardry:levitation.desc=Raises the caster upwards while the use item button is held. Will also negate fall damage if used before hitting the ground. +spell.wizardry:life_drain.desc=Creates a stream of withering energy in the direction you are pointing which drains the life of the target and uses it to gradually regenerate your health. +spell.wizardry:light.desc=Creates a magical point of light which illuminates the surrounding area. Lasts for 30 seconds. +spell.wizardry:lightning_arrow.desc=Shoots an arrow of lightning in the direction you are pointing. +spell.wizardry:lightning_bolt.desc=Causes lightning to strike where you are pointing. +spell.wizardry:lightning_disc.desc=Sends a disk of lightning flying off in the direction you are pointing, which seeks targets. +spell.wizardry:lightning_hammer.desc="I smite you by the wrath of the heavens!" +spell.wizardry:lightning_pulse.desc=Charges the ground around the caster with lightning, damaging and repelling nearby creatures. +spell.wizardry:lightning_ray.desc=Creates a stream of lightning in the direction you are pointing which continually damages targets. +spell.wizardry:lightning_sigil.desc=Places a magical lightning trap on the ground which damages the creature that triggers it and chains lightning to other nearby creatures. +spell.wizardry:lightning_web.desc="Focus. Channel the storm in your mind through your wand and unleash its fury." +spell.wizardry:magic_missile.desc=Fires a bolt of magical energy in the direction you are pointing. +spell.wizardry:metamorphosis.desc=Changes the target into another form. Only works on some creatures. +spell.wizardry:meteor.desc=Some wizards just want to see the world burn... +spell.wizardry:mind_control.desc=Takes control of the target's mind for 30 seconds, causing it switch sides and fight for the caster instead. Will not work on creatures that are too strong-willed. +spell.wizardry:mind_trick.desc=Confuses and disorients the target for 15 seconds, rendering it unable to attack effectively. The effect will be dispelled if the target takes damage. +spell.wizardry:none.desc=To get a spell book with the /give command, use metadata: /give [player] wizardry:spell_book 1 [spell id] (if you found this book in a chest, some other mod has messed things up). +spell.wizardry:oakflesh.desc=Improves the caster's damage resistance for 30 seconds. +spell.wizardry:petrify.desc=Turns the target to stone until broken out, with a chance for it to break out when it is dark. The target cannot move or do anything while petrified but is also impervious to all damage. +spell.wizardry:phase_step.desc=Teleports the caster through a 1 block thick wall in front of them. Range upgrades will increase the thickness you can teleport through. +spell.wizardry:plague_of_darkness.desc=The darkness will consume them all... +spell.wizardry:pocket_furnace.desc=Smelts up to 5 smeltable items in the caster's inventory. Items on the hotbar will be smelted first. +spell.wizardry:pocket_workbench.desc=Allows the caster to craft items as if they were at a crafting table. +spell.wizardry:poison.desc=Fires poison in the direction you are pointing. +spell.wizardry:poison_bomb.desc=Lanches a poison bomb in the direction you are pointing which explodes on impact, poisoning nearby creatures. +spell.wizardry:replenish_hunger.desc=Replenishes the caster's food level by 6 hunger points. +spell.wizardry:ring_of_fire.desc=Creates a ring of fire around the caster, damaging all nearby enemies and setting them on fire. +spell.wizardry:shadow_ward.desc=Creates a wall of darkness in front of the caster which causes half of all incoming damage to be inflicted upon the attacker instead. +spell.wizardry:shield.desc=Creates a protective barrier of force that blocks projectiles and magic. Also grants the caster a weak resistance effect. +spell.wizardry:shockwave.desc=Boom. +spell.wizardry:silverfish_swarm.desc="Ahhhh! They're MULTIPLYING!" +spell.wizardry:sixth_sense.desc=Allows the caster to sense the locations of nearby creatures, even through walls, for 20 seconds. +spell.wizardry:slime.desc=Engulfs the target in slime which slows and continually damages it. The slime bursts after 10 seconds. +spell.wizardry:smoke_bomb.desc=Launches a smoke bomb in the direction you are pointing which explodes on impact, releasing smoke and blinding nearby creatures for a short time. +spell.wizardry:snare.desc=Sets a trap on the ground which damages and briefly slows the creature that triggers it. +spell.wizardry:snowball.desc=Launches a snowball in the direction you are pointing. +spell.wizardry:spark_bomb.desc=Launches a shock charge in the direction you are pointing which releases sparks at nearby enemies on impact. +spell.wizardry:spectral_pathway.desc=Creates an indestructible magical bridge in front of you which extends for 15 blocks. The bridge vanishes after 60 seconds. +spell.wizardry:spider_swarm.desc=Summons a swarm of venomous spiders to fight for you. The spiders will disappear after 30 seconds or if they are killed. +spell.wizardry:static_aura.desc=Surrounds the caster with lightning for 30 seconds, firing a spark of lightning at anything that hits them. +spell.wizardry:summon_blaze.desc=Summons a blaze to fight for you. The blaze will disappear after 30 seconds or if it is killed. +spell.wizardry:summon_ice_giant.desc="Smash them!" +spell.wizardry:summon_ice_wraith.desc=Summons an ice wraith to fight for you. The ice wraith will disappear after 30 seconds or if it is killed. +spell.wizardry:summon_iron_golem.desc=Automatic automated autonomous automaton. +spell.wizardry:summon_lightning_wraith.desc=Summons a lightning wraith to fight for you. The lightning wraith will disappear after 30 seconds or if it is killed. +spell.wizardry:summon_phoenix.desc=From the ashes... +spell.wizardry:summon_shadow_wraith.desc=Summons a shadow wraith to fight for you. +spell.wizardry:summon_skeleton.desc=Summons a skeleton to fight for you. The skeleton will disappear after 30 seconds or if it is killed. +spell.wizardry:summon_skeleton_legion.desc="Rise, undead army!" +spell.wizardry:summon_snow_golem.desc=Creates a snow golem to fight for you. Lasts until the snow golem dies. +spell.wizardry:summon_spirit_horse.desc=Summons a spirit horse for you to ride. The spirit horse will vanish a short while after it is dismounted, or you can dismiss it by shift-right-clicking on it with any wand. +spell.wizardry:summon_spirit_wolf.desc=Summons a spirit wolf companion to fight for you. The spirit wolf will only disappear if it is killed, or you can dismiss it by shift-right-clicking on it with any wand. +spell.wizardry:summon_storm_elemental.desc="Storm Elemental: An ancient manifestation of the elements, it can hardly contain the raw power churning within it." - The Wizard's Guide to Arcane Beings, Volume I_i +spell.wizardry:summon_wither_skeleton.desc=Summons a wither skeleton to fight for you. The wither skeleton will disappear after 30 seconds or if it is killed. +spell.wizardry:summon_zombie.desc=Summons a zombie to fight for you. The zombie will disappear after 30 seconds or if it is killed. +spell.wizardry:telekinesis.desc=Moves an item or other small object towards you, or right-clicks the block you are looking at. Can also be used to disarm players. +spell.wizardry:thunderbolt.desc=Shoots a bolt of thunder which knocks back targets. +spell.wizardry:thunderstorm.desc="Mwahahahahahaha!" +spell.wizardry:tornado.desc=Unleashes a tornado in the direction you are pointing which hurls anything in its path skywards. +spell.wizardry:transience.desc=Makes the caster transient for 20 seconds. The caster is immune to all damage while transient but cannot break or place blocks or cause any damage. +spell.wizardry:transportation.desc=Transports the caster to their remembered stone circle. To use this spell, make a circle of stones of transportation, then right click it with a wand. +spell.wizardry:vanishing_box.desc=Grants the caster access to their ender chest storage. +spell.wizardry:wall_of_frost.desc=Winter at your fingertips. +spell.wizardry:water_breathing.desc=Allows the caster to breathe underwater for 60 seconds. +spell.wizardry:whirlwind.desc=Causes the target to be blown upwards and away from you at speed. +spell.wizardry:wither.desc=Fires a ray of darkness which withers anything it touches. +spell.wizardry:wither_skull.desc=Launches a wither skull in the direction you are pointing. + +spell.wizardry:invoke_weather.sun=The rain begins to stop... +spell.wizardry:invoke_weather.rain=The heavens open... +spell.wizardry:transportation.missing=Your remembered stone circle is missing or obstructed... +spell.wizardry:transportation.undefined=You must remember the location of a stone circle first! +spell.wizardry:transportation.wrongdimension=Your remembered stone circle is in another dimension... +spell.wizardry:clairvoyance.searching=Searching... +spell.wizardry:clairvoyance.confirm=The path revealed upon casting %1$s will now lead back to this point +spell.wizardry:clairvoyance.outofrange=Your remembered location is too far away or inaccessible... +spell.wizardry:clairvoyance.undefined=You must remember a location first! +spell.wizardry:clairvoyance.wrongdimension=Your remembered location is in another dimension... + +potion.wizardry:frost=Frostbite +potion.wizardry:fireskin=Fireskin +potion.wizardry:ice_shroud=Ice Shroud +potion.wizardry:static_aura=Static Aura +potion.wizardry:transience=Transience +potion.wizardry:decay=Decay +potion.wizardry:sixth_sense=Sixth Sense +potion.wizardry:arcane_jammer=Arcane Jammer +potion.wizardry:mind_trick=Mind Trick +potion.wizardry:mind_control=Mind Control +potion.wizardry:font_of_mana=Font of Mana +potion.wizardry:fear=Fear + +enchantment.wizardry:magic_sword=Imbuement +enchantment.wizardry:magic_bow=Imbuement +enchantment.wizardry:flaming_weapon=Fire Imbuement +enchantment.wizardry:freezing_weapon=Frost Imbuement + +key.categories.wizardry=Wizardry + +key.wizardry:next_spell=Next Spell +key.wizardry:previous_spell=Previous Spell + +death.attack.wizardry_magic=%1$s was killed by %2$s using magic +death.attack.indirect_wizardry_magic=%1$s was killed by %2$s using magic + +commands.wizardry:cast.usage=/%1$s [player] [damage multiplier] [range multiplier] [duration multiplier] [blast multiplier] +commands.wizardry:cast.success=Successfully cast %1$s +commands.wizardry:cast.success_continuous=Successfully cast %1$s; repeat the command to stop +commands.wizardry:cast.success_remote=Successfully cast %1$s as %2$s +commands.wizardry:cast.success_remote_continuous=Successfully cast %1$s as %2$s; repeat the command to stop +commands.wizardry:cast.fail=Unable to cast %1$s +commands.wizardry:cast.not_found=There is no such spell with ID %1$s +commands.wizardry:cast.tag_error=Data tag parsing failed: %s + +commands.wizardry:ally.usage=/%1$s [player] +commands.wizardry:ally.addally=%1$s has been added to %2$s's list of allies +commands.wizardry:ally.removeally=%1$s has been removed from %2$s's list of allies +commands.wizardry:ally.self=Players cannot be an ally of themselves! +commands.wizardry:ally.permission=You do not have permission to change other players' allies + +commands.wizardry:allies.usage=/%1$s [player] +commands.wizardry:allies.list=Players allied to you: %1$s +commands.wizardry:allies.list_other=Players allied to %1$s: %2$s +commands.wizardry:allies.permission=You do not have permission to view other players' allies +commands.wizardry:allies.none=None + +commands.wizardry:discoverspell.usage=/%1$s [player] +commands.wizardry:discoverspell.not_found=There is no such spell with ID %1$s +commands.wizardry:discoverspell.clear=Cleared all spell discovery data for %1$s +commands.wizardry:discoverspell.all=Added all spells to %1$s's spell discovery data +commands.wizardry:discoverspell.addspell=Added %1$s to %2$s's spell discovery data +commands.wizardry:discoverspell.removespell=Removed %1$s from %2$s's spell discovery data + +config.wizardry.title.general=Mod Options + +config.wizardry.category.spells=Configure Spells +config.wizardry.category.spells.tooltip=Select which spells are enabled +config.wizardry.title.spells=Spell Configuration +config.wizardry.subtitle.spells=Set a spell to false to disable it. + +config.wizardry.category.resistances=Configure Resistances +config.wizardry.category.resistances.tooltip=Configure which mobs are immune to different types of magic +config.wizardry.title.resistances=Resistance Configuration +config.wizardry.subtitle.resistances=See descriptions of individual options for more details. + +config.wizardry.category.ids=Configure IDs +config.wizardry.category.ids.tooltip=Change the IDs used by wizardry +config.wizardry.title.ids=ID Configuration +config.wizardry.subtitle.ids=Change these IDs if they conflict with another mod. + +config.wizardry.tower_rarity=Tower Rarity +config.wizardry.ore_dimensions=Ore Dimensions +config.wizardry.flower_dimensions=Flower Dimensions +config.wizardry.tower_dimensions=Tower Dimensions +config.wizardry.spell_book_drop_chance=Spell Book Drop Chance +config.wizardry.generate_loot=Generate Loot +config.wizardry.firebomb_is_craftable=Firebomb Is Craftable +config.wizardry.poison_bomb_is_craftable=Poison Bomb Is Craftable +config.wizardry.smoke_bomb_is_craftable=Smoke Bomb Is Craftable +config.wizardry.use_alternate_scroll_recipe=Use Alternate Scroll Recipe +config.wizardry.teleport_through_unbreakable_blocks=Teleport Through Unbreakable Blocks +config.wizardry.show_summoned_creature_names=Show Summoned Creature Names +config.wizardry.friendly_fire=Friendly Fire +config.wizardry.telekinetic_disarmament=Telekinetic Disarmament +config.wizardry.discovery_mode=Discovery Mode +config.wizardry.enable_shift_scrolling=Enable Shift-_scrolling +config.wizardry.minion_revenge_targeting=Minion Revenge Targeting +config.wizardry.player_damage_scaling=Player Damage Scaling Factor +config.wizardry.npc_damage_scaling=NPC Damage Scaling Factor +config.wizardry.cast_command_multiplier_limit=Cast Command Multiplier Limit +config.wizardry.summoned_creature_targets_whitelist=Summoned Creature Target Whitelist +config.wizardry.summoned_creature_targets_blacklist=Summoned Creature Target Blacklist +config.wizardry.spell_hud_position=Spell HUD Position +config.wizardry.cast_command_name=Cast Spell Command Name +config.wizardry.discoverspell_command_name=Discover Spell Command Name +config.wizardry.ally_command_name=Set Ally Command Name +config.wizardry.allies_command_name=View Allies Command Name + +config.wizardry.mobs_immune_to_fire=Mobs Immune To Fire +config.wizardry.mobs_immune_to_ice=Mobs Immune To Ice +config.wizardry.mobs_immune_to_lightning=Mobs Immune To Lightning +config.wizardry.mobs_immune_to_wither=Mobs Immune To Wither +config.wizardry.mobs_immune_to_poison=Mobs Immune To Poison + +config.wizardry.tower_rarity.tooltip=Rarity of wizard towers. Higher numbers are rarer. Set to 0 to disable wizard towers completely. +config.wizardry.ore_dimensions.tooltip=List of dimension ids in which crystal ore will generate. Note that removing the overworld (id 0) from this list will make the mod VERY difficult to play! +config.wizardry.flower_dimensions.tooltip=List of dimension ids in which crystal flowers will generate. +config.wizardry.tower_dimensions.tooltip=List of dimension ids in which wizard towers will generate. +config.wizardry.spell_book_drop_chance.tooltip=The chance for mobs to drop a spell book when killed. The greater this number, the more often they will drop. Set to 0 to disable spell book drops. Set to 200 for guaranteed drops. +config.wizardry.generate_loot.tooltip=Whether to generate wizardry loot in dungeon chests. +config.wizardry.firebomb_is_craftable.tooltip=Whether firebombs can be crafted or not. +config.wizardry.poison_bomb_is_craftable.tooltip=Whether poison bombs can be crafted or not. +config.wizardry.smoke_bomb_is_craftable.tooltip=Whether smoke bombs can be crafted or not. +config.wizardry.use_alternate_scroll_recipe.tooltip=Whether to require a magic crystal in the shapeless crafting recipe for blank scrolls. Set to true if another mod adds a conflicting recipe. +config.wizardry.teleport_through_unbreakable_blocks.tooltip=Whether players are allowed to teleport through unbreakable blocks (e.g. bedrock) using the phase step spell. +config.wizardry.show_summoned_creature_names.tooltip=Whether to show summoned creatures' names and owners above their heads. +config.wizardry.friendly_fire.tooltip=Whether to allow players to damage their designated allies using magic. +config.wizardry.telekinetic_disarmament.tooltip=Whether to allow players to disarm other players using the telekinesis spell.wizardry: Set to false to prevent stealing of items. +config.wizardry.discovery_mode.tooltip=For those who like a sense of mystery! When set to true, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is false. +config.wizardry.enable_shift_scrolling.tooltip=Whether you can switch between spells on a wand by scrolling with the mouse wheel while sneaking. Note that this will only affect you; other players connected to the same server obey their own settings. +config.wizardry.minion_revenge_targeting.tooltip=Whether summoned creatures can revenge attack their owner if their owner attacks them. +config.wizardry.player_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by players casting spells, relative to 1. +config.wizardry.npc_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1. +config.wizardry.cast_command_multiplier_limit.tooltip=Upper limit for the multipliers passed into the /cast command. This is here to stop players from accidentally breaking a world/server. Large blast mutipliers can cause extreme lag - you have been warned! +config.wizardry.summoned_creature_targets_whitelist.tooltip=List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). +config.wizardry.summoned_creature_targets_blacklist.tooltip=List of names of entities which summoned creatures and wizards are specifically not allowed to attack, overriding the defaults and the whitelist. Add creatures to this list if allowing them to be attacked causes problems or is too destructive (removing creepers from this list is done at your own risk!). Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). +config.wizardry.spell_hud_position.tooltip=The position of the spell HUD. +config.wizardry.cast_command_name.tooltip=The name of the /cast command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /cast you would type /magic instead. +config.wizardry.discoverspell_command_name.tooltip=The name of the /discoverspell command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /discoverspell you would type /magic instead. +config.wizardry.ally_command_name.tooltip=The name of the /ally command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /ally you would type /magic instead. +config.wizardry.allies_command_name.tooltip=The name of the /allies command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /allies you would type /magic instead. + +config.wizardry.mobs_immune_to_fire.tooltip=List of names of entities that are immune to fire, in addition to the defaults. Add mod creatures to this list if you want them to be immune to fire magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). +config.wizardry.mobs_immune_to_ice.tooltip=List of names of entities that are immune to ice, in addition to the defaults. Add mod creatures to this list if you want them to be immune to ice magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). +config.wizardry.mobs_immune_to_lightning.tooltip=List of names of entities that are immune to lightning, in addition to the defaults. Add mod creatures to this list if you want them to be immune to lightning magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). +config.wizardry.mobs_immune_to_wither.tooltip=List of names of entities that are immune to wither effects, in addition to the defaults. Add mod creatures to this list if you want them to be immune to withering magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). +config.wizardry.mobs_immune_to_poison.tooltip=List of names of entities that are immune to poison, in addition to the defaults. Add mod creatures to this list if you want them to be immune to poison magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). + +wizard.debug=%1$s, %2$s, %3$s \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/lang/es_ES.lang b/src/main/resources/assets/wizardry/lang/es_ES.lang new file mode 100644 index 00000000..7608a50f --- /dev/null +++ b/src/main/resources/assets/wizardry/lang/es_ES.lang @@ -0,0 +1,736 @@ +tile.wizardry:arcane_workbench.name=Mesa de Trabajo Arcana +tile.wizardry:crystal_ore.name=Mena de Cristal Magico +tile.wizardry:petrified_stone.name=Piedra Petrificada +tile.wizardry:ice_statue.name=Estatua de Hielo +tile.wizardry:crystal_flower.name=Flor de Cristal Magico +tile.wizardry:snare.name=Trampa +tile.wizardry:transportation_stone.name=Piedra de Transportacion +tile.wizardry:spectral_block.name=Bloque Espectral +tile.wizardry:crystal_block.name=Bloque de Cristal Magico + +item.wizardry:magic_crystal.name=Cristal Magico +item.wizardry:magic_wand.name=Varita Magica +item.wizardry:apprentice_wand.name=Varita de Aprendiz +item.wizardry:advanced_wand.name=Varita Avanzada +item.wizardry:master_wand.name=Varita de Maestro +item.wizardry:spell_book.name=Libro de Hechizo + +item.wizardry:arcane_tome.name=Tomo de lo Arcano +item.wizardry:arcane_tome.desc1=Mejora cualquier %1$s +item.wizardry:arcane_tome.desc2=varita a nivel %1$s + +item.wizardry:wizard_handbook.name=El Manual del Hechizero +item.wizardry:wizard_handbook.desc=por %1$s + +item.wizardry:wand.buff=+%1$s %2$s de potencia +item.wizardry:wand.spell=Hechizo actual: %1$s +item.wizardry:wand.mana=Mana: %1$s/%2$s + +item.wizardry:wand.addally=%1$s ha sido anadido a tu lista de aliados +item.wizardry:wand.removeally=%1$s ha sido removido a tu lista de aliados + +item.wizardry:basic_fire_wand.name=Varita de Ascuas +item.wizardry:basic_ice_wand.name=Varita de Escarcha +item.wizardry:basic_lightning_wand.name=Varita de Chispas +item.wizardry:basic_necromancy_wand.name=Varita de las Sombras +item.wizardry:basic_earth_wand.name=Varita del Bosque +item.wizardry:basic_sorcery_wand.name=Varita del Misterio +item.wizardry:basic_healing_wand.name=Varita de la Curacion + +item.wizardry:apprentice_fire_wand.name=Varita del Piromano Aprendiz +item.wizardry:apprentice_ice_wand.name=Varita del Mago de Hielo Aprendiz +item.wizardry:apprentice_lightning_wand.name=Varita del Mago de Tormenta Aprendiz +item.wizardry:apprentice_necromancy_wand.name=Varita del Nigromante Aprendiz +item.wizardry:apprentice_earth_wand.name=Varita del Mago de Tierra Aprendiz +item.wizardry:apprentice_sorcery_wand.name=Varita del Hechicero Aprendiz +item.wizardry:apprentice_healing_wand.name=Varita del Sanador Aprendiz + +item.wizardry:advanced_fire_wand.name=Varita del Piromano +item.wizardry:advanced_ice_wand.name=Varita del Mago de Hielo +item.wizardry:advanced_lightning_wand.name=Varita del Mago de Tormenta +item.wizardry:advanced_necromancy_wand.name=Varita del Nigromante +item.wizardry:advanced_earth_wand.name=Varita del Mago de Tierra +item.wizardry:advanced_sorcery_wand.name=Varita del Hechicero +item.wizardry:advanced_healing_wand.name=Varita del Sanador + +item.wizardry:master_fire_wand.name=Varita del Piromano Maestro +item.wizardry:master_ice_wand.name=Varita del Mago de Hielo Maestro +item.wizardry:master_lightning_wand.name=Varita del Mago de Tormenta Maestro +item.wizardry:master_necromancy_wand.name=Varita del Nigromante Maestro +item.wizardry:master_earth_wand.name=Varita del Mago de Tierra Maestro +item.wizardry:master_sorcery_wand.name=Varita del Hechicero Maestro +item.wizardry:master_healing_wand.name=Varita del Sanador Maestro + +item.wizardry:spectral_sword.name=Espada Espectral +item.wizardry:spectral_pickaxe.name=Pico Espectral +item.wizardry:spectral_bow.name=Arco Espectral + +item.wizardry:mana_flask.name=Frasco de Mana +item.wizardry:storage_upgrade.name=Mejora de Almacenamiento para Varita +item.wizardry:siphon_upgrade.name=Mejora de Sifon para Varita +item.wizardry:condenser_upgrade.name=Mejora de Condensador para Varita +item.wizardry:range_upgrade.name=Mejora de Rango para Varita +item.wizardry:duration_upgrade.name=Mejora de Duracion para Varita +item.wizardry:cooldown_upgrade.name=Mejora de Tiempo de Reactivacion para Varita +item.wizardry:blast_upgrade.name=Mejora de Explosion para Varita +item.wizardry:attunement_upgrade.name=Mejora de Sintonizacion para Varita + +item.wizardry:flaming_axe.name=Hacha Llameante +item.wizardry:frost_axe.name=Hacha Escarchada + +item.wizardry:firebomb.name=Bomba de fuego +item.wizardry:poison_bomb.name=Bomba de veneno +item.wizardry:smoke_bomb.name=Bomba de humo + +item.wizardry:blank_scroll.name=Pergamino en blanco +item.wizardry:scroll.name=Pergamino de %1$s +item.wizardry:scroll.undiscovered.name=Pergamino "%1$s" +item.wizardry:identification_scroll.name=Pergamino de la Identificacion +item.wizardry:identification_scroll.desc1=%1$sIdentifica un libro o pergamino +item.wizardry:identification_scroll.desc2=%1$sdesconocido +item.wizardry:identification_scroll.nothing_to_identify=Nada que identificar! + +item.wizardry:armour_upgrade.name=Sello Arcano de la Proteccion +item.wizardry:armour_upgrade.desc1=%1$sMejora cualquier armadura +item.wizardry:armour_upgrade.desc2=%1$spara volverla %2$slegendaria + +item.wizardry:magic_silk.name=Seda Magica + +item.wizardry:wizard_armour.legendary=Legendario +item.wizardry:wizard_armour.buff=-%1$s %2$s costo +item.wizardry:wizard_armour.mana=Mana: %1$s/%2$s + +item.wizardry:wizard_hat.name=Sombrero de Mago +item.wizardry:wizard_robe.name=Tunica de Mago +item.wizardry:wizard_leggings.name=Pantalones de Mago +item.wizardry:wizard_boots.name=Botas de Mago + +item.wizardry:wizard_hat_fire.name=Sombrero de Piromano +item.wizardry:wizard_robe_fire.name=Tunica de Piromano +item.wizardry:wizard_leggings_fire.name=Pantalones de Piromano +item.wizardry:wizard_boots_fire.name=Botas de Piromano + +item.wizardry:wizard_hat_ice.name=Sombrero de Mago de Hielo +item.wizardry:wizard_robe_ice.name=Tunica de Mago de Hielo +item.wizardry:wizard_leggings_ice.name=Pantalones de Mago de Hielo +item.wizardry:wizard_boots_ice.name=Botas de Mago de Hielo + +item.wizardry:wizard_hat_lightning.name=Sombrero de Mago de Tormenta +item.wizardry:wizard_robe_lightning.name=Tunica de Mago de Tormenta +item.wizardry:wizard_leggings_lightning.name=Pantalones de Mago de Tormenta +item.wizardry:wizard_boots_lightning.name=Botas de Mago de Tormenta + +item.wizardry:wizard_hat_necromancy.name=Sombrero de Nigromante +item.wizardry:wizard_robe_necromancy.name=Tunica de Nigromante +item.wizardry:wizard_leggings_necromancy.name=Pantalones de Nigromante +item.wizardry:wizard_boots_necromancy.name=Botas de Nigromante + +item.wizardry:wizard_hat_earth.name=Sombrero de Mago de Tierra +item.wizardry:wizard_robe_earth.name=Tunica de Mago de Tierra +item.wizardry:wizard_leggings_earth.name=Pantalones de Mago de Tierra +item.wizardry:wizard_boots_earth.name=Botas de Mago de Tierra + +item.wizardry:wizard_hat_sorcery.name=Sombrero de Hechicero +item.wizardry:wizard_robe_sorcery.name=Tunica de Hechicero +item.wizardry:wizard_leggings_sorcery.name=Pantalones de Hechicero +item.wizardry:wizard_boots_sorcery.name=Botas de Hechicero + +item.wizardry:wizard_hat_healing.name=Sombrero de Sanador +item.wizardry:wizard_robe_healing.name=Tunica de Sanador +item.wizardry:wizard_leggings_healing.name=Pantalones de Sanador +item.wizardry:wizard_boots_healing.name=Botas de Sanador + +item.wizardry:spawn_wizard.name=Invocar Mago +item.wizardry:spawn_evil_wizard.name=Invocar Mago Malvado + +item.wizardry:spectral_helmet.name=Casco Espectral +item.wizardry:spectral_chestplate.name=Pechera Espectral +item.wizardry:spectral_leggings.name=Grebas Espectrales +item.wizardry:spectral_boots.name=Botas Espectrales + +entity.wizardry.summonedcreature.nameplate=%2$s de %1$s + +entity.wizardry.zombie_minion.name=Zombi +entity.wizardry.skeleton_minion.name=Esqueleto +entity.wizardry.spider_minion.name=Arana +entity.wizardry.blaze_minion.name=Blaze +entity.wizardry.ice_wraith.name=Espectro de Hielo +entity.wizardry.lightning_wraith.name=Espectro de Relampago +entity.wizardry.shadow_wraith.name=Espectro de las Sombras +entity.wizardry.spirit_wolf.name=Lobo Espiritual +entity.wizardry.spirit_horse.name=Caballo Espiritual +entity.wizardry.ice_giant.name=Gigante de Hielo +entity.wizardry.phoenix.name=Fenix +entity.wizardry.wizard.name=Mago +entity.wizardry.magic_slime.name=Slime Magico +entity.wizardry.silverfish_minion.name=Lepisma +entity.wizardry.storm_elemental.name=Elemental de Tormenta +entity.wizardry.evil_wizard.name=Mago Malvado +entity.wizardry.decoy.name=Senuelo + +entity.wizardry.magic_missile.name=Magia +entity.wizardry.arc.name=Magia +entity.wizardry.spark_bomb.name=Magia +entity.wizardry.ice_shard.name=Magia +entity.wizardry.firebomb.name=Magic +entity.wizardry.poison_bomb.name=Magia +entity.wizardry.force_orb.name=Magia +entity.wizardry.spark.name=Magia +entity.wizardry.darkness_orb.name=Magia +entity.wizardry.fire_sigil.name=Magia +entity.wizardry.frost_sigil.name=Magia +entity.wizardry.lightning_sigil.name=Magia +entity.wizardry.lightning_arrow.name=Magia +entity.wizardry.firebolt.name=Magia +entity.wizardry.ice_charge.name=Magia +entity.wizardry.force_arrow.name=Magia +entity.wizardry.dart.name=Magia +entity.wizardry.lightning_disc.name=Magia +entity.wizardry.thunderbolt.name=Magia +entity.wizardry.decay.name=Magia +entity.wizardry.ice_lance.name=Magia +entity.wizardry.smoke_bomb.name=Magia +entity.wizardry.ice_spike.name=Magia + +entity.wizardry.black_hole.name=Agujero Negro +entity.wizardry.shield.name=Escudo +entity.wizardry.meteor.name=Meteoro +entity.wizardry.blizzard.name=Ventisca +entity.wizardry.bubble.name=Burbuja +entity.wizardry.tornado.name=Tornado +entity.wizardry.lightning_hammer.name=Martillo Relampago +entity.wizardry.arrow_rain.name=Lluvia de Flechas +entity.wizardry.healing_aura.name=Aura Curador +entity.wizardry.forcefield.name=Campo de Fuerza +entity.wizardry.ring_of_fire.name=Anillo de Fuego +entity.wizardry.earthquake.name=Terremoto +entity.wizardry.falling_grass.name=Hierba Descendiente +entity.wizardry.hailstorm.name=Tormenta de Granizo +entity.wizardry.lightning_pulse.name=Pulso de Relampago + +item_group.wizardry=Wizardry +item_group.wizardryspells=Hechizos de Wizardry + +achievement.crystal=Un Cristral Curioso... +achievement.crystal.desc=Pica un Cristal Magico +achievement.arcane_initiate=Inicio Arcano +achievement.arcane_initiate.desc=Craftea una varita magica con una pepita de oro, un palo y un cristal magico +achievement.apprentice=Aprendiz de Mago +achievement.apprentice.desc=Usa un tomo de lo arcano para mejorar tu varita +achievement.master=Maestro Arcano +achievement.master.desc=Obten una varita de maestro +achievement.all_spells=Mago de Todos los Oficios +achievement.all_spells.desc=Lanza todos los hechizos disponibles +achievement.wizard_trade=Comercio Magico +achievement.wizard_trade.desc=Compra un objeto a un mago +achievement.buy_master_spell=El Conocimiento es Poder +achievement.buy_master_spell.wizardry:desc=Compra un hechizo maestro a un mago +achievement.freeze_blaze=Ya No Estas Tan Caliente +achievement.freeze_blaze.desc=Congela a un blaze +achievement.charge_creeper=Va a Explotar!! +achievement.charge_creeper.desc='Accidentalmente' carga un creeper +achievement.frankenstein=Frankenstein +achievement.frankenstein.desc=Convierte a un cerdo en un hombrecerdo zombi usando el hechizo de rayo +achievement.special_upgrade=Remiendo Arcano +achievement.special_upgrade.desc=Aplica una mejora a una varita +achievement.craft_flask=Es Magia, Embotellada! +achievement.craft_flask.desc=Craftea un frasco de mana +achievement.elemental=Elemental +achievement.elemental.desc=Obten una varita elemental +achievement.armour_set=Now Eres un Mago Apropiado +achievement.armour_set.desc=Craftea y equipa el set completo de armadura de mago +achievement.legendary=Legendario +achievement.legendary.desc=Obten una pieza de armadura de mago legendaria +achievement.self_destruct=Tiro por la culata +achievement.self_destruct.desc=Muere por tu propia magia +achievement.pig_tornado=No De Nuevo... +achievement.pig_tornado.desc=Monta un cerdo hacia un tornado +achievement.jam_wizard=Sesion de Interferencia +achievement.jam_wizard.desc=Usa el hechizo arcano de interferencia en un mago +achievement.slime_skeleton=Situacion Pegajosa +achievement.slime_skeleton.desc=Sumerge un esqueleto en slime +achievement.anger_wizard=Te Arrepentiras de Eso +achievement.anger_wizard.desc=Haz que un mago se enoje +achievement.defeat_evil_wizard=Justicia +achievement.defeat_evil_wizard.desc=Derrota a un mago malvado +achievement.max_out_wand=Equipada Al Maximo! +achievement.max_out_wand.desc=Aplica el maximo numero de mejoras a una varita maestra +achievement.element_master=Maestria Elemental +achievement.element_master.desc=Lanza todos los hechizos de cualquier elemento +achievement.identify_spell=Apreciacion Arcana +achievement.identify_spell.wizardry:desc=Usa un pergamino de la identificacion para identificar un libro de hechizos o pergamino + +tile.wizardry:transportation_stone.confirm=A partir de ahora retornaras a este lugar cuando lances %1$s +tile.wizardry:transportation_stone.invalid=Primero debes hacer un circulo con 8 piedras de transportacion! + +container.wizardry:arcane_workbench=Mesa de Trabajo Arcana +container.wizardry:arcane_workbench.apply=Aplicar +container.wizardry:arcane_workbench.mana=Mana: +container.wizardry:arcane_workbench.upgrades=Mejoras Aplicadas: + +tier.basic=Novato +tier.apprentice=Aprendiz +tier.advanced=Avanzado +tier.master=Maestro + +element.simple=Ninguno +element.fire=Fuego +element.ice=Hielo +element.lightning=Rayo +element.necromancy=Nigromancia +element.earth=Tierra +element.sorcery=Hechiceria +element.healing=Curacion + +element.simple.wizard=Mago +element.fire.wizard=Piromano +element.ice.wizard=Mago de Hielo +element.lightning.wizard=Mago de Tormenta +element.necromancy.wizard=Nigromante +element.earth.wizard=Mago de Tierra +element.sorcery.wizard=Hechicero +element.healing.wizard=Curador + +spelltype.attack=Ataque +spelltype.defence=Defensa +spelltype.utility=Utilidad +spelltype.minion=Secuaz + +spell.disabled=%1$s ha sido desactivado en la configuracion +spell.resist=%1$s resistio %2$s +spell.discover=Descubrio el hechizo %1$s! + +spell.wizardry:agility=Agilidad +spell.wizardry:arc=Arco Electrico +spell.wizardry:arcane_jammer=Perturbador Arcano +spell.wizardry:arrow_rain=Lluvia de Flechas +spell.wizardry:banish=Desaparecer +spell.wizardry:black_hole=Agujero Negro +spell.wizardry:blink=Parpadeo +spell.wizardry:blizzard=Ventisca +spell.wizardry:bubble=Burbuja +spell.wizardry:chain_lightning=Cadena de Rayos +spell.wizardry:clairvoyance=Clarividencia +spell.wizardry:cobwebs=Telas de Araña +spell.wizardry:conjure_armour=Conjurar Armadura +spell.wizardry:conjure_bow=Conjurar Arco +spell.wizardry:conjure_pickaxe=Conjurar Pico +spell.wizardry:conjure_sword=Conjurar Espada +spell.wizardry:cure_effects=Purificar Efectos +spell.wizardry:curse_of_soulbinding=Maldicion de Atadura al Alma +spell.wizardry:darkness_orb=Orbe de la Oscuridad +spell.wizardry:darkvision=Vision Oscura +spell.wizardry:dart=Dardo +spell.wizardry:decay=Deterioro +spell.wizardry:decoy=Senuelo +spell.wizardry:detonate=Detonar +spell.wizardry:diamondflesh=Piel de Diamante +spell.wizardry:earthquake=Terremoto +spell.wizardry:entrapment=Atrapamiento +spell.wizardry:fireball=Bola de Fuego +spell.wizardry:firebolt=Disparo de Fuego +spell.wizardry:firebomb=Bomba de Fuego +spell.wizardry:fire_resistance=Resistencia al Fuego +spell.wizardry:fire_sigil=Sigilo de Fuego +spell.wizardry:fireskin=Piel de Fuego +spell.wizardry:firestorm=Tormenta de Fuego +spell.wizardry:flame_ray=Rayo de Fuego +spell.wizardry:flaming_axe=Hacha Llameante +spell.wizardry:flaming_weapon=Arma Llameante +spell.wizardry:flight=Vuelo +spell.wizardry:font_of_mana=Fuente de Mana +spell.wizardry:font_of_vitality=Fuente de Vitalidad +spell.wizardry:force_arrow=Flecha de Fuerza +spell.wizardry:forcefield=Campo de Fuerza +spell.wizardry:force_orb=Orbe de Fuerza +spell.wizardry:forests_curse=Maldicion del Bosque +spell.wizardry:freeze=Congelar +spell.wizardry:freezing_weapon=Arma Congelante +spell.wizardry:frost_axe=Hacha Escarchada +spell.wizardry:frost_ray=Rayo de Escarcha +spell.wizardry:frost_sigil=Sigilo de Escarcha +spell.wizardry:glide=Planear +spell.wizardry:greater_fireball=Bola de Fuego Mayor +spell.wizardry:greater_heal=Curacion Mayor +spell.wizardry:group_heal=Curacion Grupal +spell.wizardry:growth_aura= Aura de Crecimiento +spell.wizardry:hailstorm=Tormenta de Granizo +spell.wizardry:heal=Curar +spell.wizardry:heal_ally=curar Aliado +spell.wizardry:healing_aura=Aura de Curacion +spell.wizardry:homing_spark=Chispa Dirigida +spell.wizardry:ice_age=Era de Hielo +spell.wizardry:ice_charge=Carga de Hielo +spell.wizardry:ice_lance=Lanza de Hielo +spell.wizardry:ice_shard=Fragmento de Hielo +spell.wizardry:ice_shroud=Velo de Hielo +spell.wizardry:ice_spikes=Pinchos de Hielo +spell.wizardry:ice_statue=Estatua de Hielo +spell.wizardry:ignite=Encender +spell.wizardry:imbue_weapon=Imbuir Arma +spell.wizardry:intimidate=Intimidar +spell.wizardry:invigorating_presence=Presencia Vigorizante +spell.wizardry:invisibility=Invisibilidad +spell.wizardry:invoke_weather=Invocar clima +spell.wizardry:ironflesh=Piel de Hierro +spell.wizardry:leap=Salto +spell.wizardry:levitation=Levitacion +spell.wizardry:life_drain=Drenaje de Vida +spell.wizardry:light=Luz +spell.wizardry:lightning_arrow=Flecha de Rayo +spell.wizardry:lightning_bolt=Disparo de Rayo +spell.wizardry:lightning_disc=Disco de Rayo +spell.wizardry:lightning_hammer=Martillo Relampago +spell.wizardry:lightning_pulse=Pulso Electrico +spell.wizardry:lightning_ray=Rayo Electrico +spell.wizardry:lightning_sigil=Sigilo Electrico +spell.wizardry:lightning_web=Red Electrica +spell.wizardry:magic_missile=Misil Magico +spell.wizardry:metamorphosis=Metamorfosis +spell.wizardry:meteor=Meteoro +spell.wizardry:mind_control=Control Mental +spell.wizardry:mind_trick=Truco Mental +spell.wizardry:none=[Espacio Vacio] +spell.wizardry:oakflesh=Piel de Roble +spell.wizardry:petrify=Petrificar +spell.wizardry:phase_step=Paso de Fase +spell.wizardry:plague_of_darkness=Plaga de la Oscuridad +spell.wizardry:pocket_furnace=Horno del Bolsillo +spell.wizardry:pocket_workbench=Mesa de Trabajo de Bolsillo +spell.wizardry:poison=Veneno +spell.wizardry:poison_bomb=Bomba de Veneno +spell.wizardry:replenish_hunger=Rellenar hambre +spell.wizardry:ring_of_fire=Anillo de Fuego +spell.wizardry:shadow_ward=Guarda de las Sombras +spell.wizardry:shield=Escudo +spell.wizardry:shockwave=Onda de Choque +spell.wizardry:silverfish_swarm=Emjambre de Lepismas +spell.wizardry:sixth_sense=Sexto Sentido +spell.wizardry:slime=Slime +spell.wizardry:smoke_bomb=Bomba de Humo +spell.wizardry:snare=Trampa +spell.wizardry:snowball=Bola de Nieve +spell.wizardry:spark_bomb=Bomba de Chispa +spell.wizardry:spectral_pathway=Camino Espectral +spell.wizardry:spider_swarm=Emjambre de Arañas +spell.wizardry:static_aura=Aura Estatica +spell.wizardry:summon_blaze=Invocar Blaze +spell.wizardry:summon_ice_giant=Invocar Gigante de Hielo +spell.wizardry:summon_ice_wraith=Invocar Espectro de Hielo +spell.wizardry:summon_iron_golem=Invocar Golem de Hierro +spell.wizardry:summon_lightning_wraith=Invocar Espectro de Rayo +spell.wizardry:summon_phoenix=Invocar Fenix +spell.wizardry:summon_shadow_wraith=Invocar Espectro de las Sombras +spell.wizardry:summon_skeleton=Invocar Esqueleto +spell.wizardry:summon_skeleton_legion=Invocar Legion de Esqueletos +spell.wizardry:summon_snow_golem=Invocar Golem de Nieve +spell.wizardry:summon_spirit_horse=Invocar Caballo Espiritual +spell.wizardry:summon_spirit_wolf=Invocar Lobo Espiritual +spell.wizardry:summon_storm_elemental=Invocar Elemental del Rayo +spell.wizardry:summon_wither_skeleton=Invocar Esqueleto del Wither +spell.wizardry:summon_zombie=Invocar Zombi +spell.wizardry:telekinesis=Telequinesis +spell.wizardry:thunderbolt=Rayo +spell.wizardry:thunderstorm=Tormenta Electrica +spell.wizardry:tornado=Tornado +spell.wizardry:transience=Transitoriedad +spell.wizardry:transportation=Transportacion +spell.wizardry:vanishing_box=Caja Desvaneciente +spell.wizardry:wall_of_frost=Pared de Escarcha +spell.wizardry:water_breathing=Respiracion Acuatica +spell.wizardry:whirlwind=Remolino de Viento +spell.wizardry:wither=Marchitar +spell.wizardry:wither_skull=Craneo de Wither + +spell.wizardry:agility.desc=Concede al mago velocidad de movimiento incrementada y un salto mas alto por 30 segundos. +spell.wizardry:arc.desc=Dispara una chispa de rayo al objetivo. +spell.wizardry:arcane_jammer.desc=No permite que el usuario use magia en los proximos 15 segundos. +spell.wizardry:arrow_rain.desc="Arqueros, disparen!" +spell.wizardry:banish.desc=Teletransporta al objetivo una posicion cercana aleatoria en contra de su voluntad. +spell.wizardry:black_hole.desc=Desgarra la realidad. +spell.wizardry:blink.desc=Teletransporta al mago a la posicion que estan apuntando. +spell.wizardry:blizzard.desc=Crea una zona de aire frio, la cual ralentiza y hiere continuamente a cualquiera en su interior. El creador del hechizo es inmune al dano pero no al efecto. +spell.wizardry:bubble.desc=Dispara un chorro de burbujas que causa que lo primero que toque flote hacia el cielo. La burbuja explota despues de un tiempo o si el objetivo es golpeado. +spell.wizardry:chain_lightning.desc=Dispara un relampago a un objetivo, el cual hace cadena con objetivos cercanos hasta dos veces. +spell.wizardry:clairvoyance.desc=Revela el camino hacia una localizacion guardada. Con este hechizo seleccionado, haga shift-clic derecho en un bloque para guardarlo. Lanza este hechizo para revelar el camino. El camino desaparecera despues de 90 segundos. +spell.wizardry:cobwebs.desc=Crea telas de arana en la direccion a la cual estas apuntando, lo cual limita la movilidad de tu objetivo. Las telas de arana despareceran en 20 segundos o si son rotas. +spell.wizardry:conjure_armour.desc=Crea Armadura Espectral alrededor del mago lo cual ofrece proteccion similar a la armadura de hierro. La armadura dura 60 segundos. El mago debe tener el espacio de armadura vacio. +spell.wizardry:conjure_bow.desc=Crea un Arco Espectral con flechas ilimitadas que dura 30 segundos. +spell.wizardry:conjure_pickaxe.desc=Crea un Pico Espectral con fuerza igual a la de un Pico de Hierro, el cual dura 30 segundos. +spell.wizardry:conjure_sword.desc=Crea una Espada Espectral con igual fuerza que una de hierro y dura 30 segundos. +spell.wizardry:cure_effects.desc=Purifica al usuario de todos los efectos de pocion, ya sean buenos o malos. +spell.wizardry:curse_of_soulbinding.desc=Provoca que el alma del objetivo esta ligada al del mago, lo que significa que todo el dano que recibe el mago lo recibe el objetivo. La maldicion dura hasta que uno de los dos muera. +spell.wizardry:darkness_orb.desc=Dispara una bala de energia oscura en la direccion que apuntes, la cual aplicar wither. +spell.wizardry:darkvision.desc=Concede vision nocturna por 45 segundos. +spell.wizardry:dart.desc=Dispara un dardo en la direccion que estas apuntando el cual hiere y debilita al objetivo. +spell.wizardry:decay.desc=Crea un pezado de deterioro en el suelo lo cual infecta y hiere a cualquier criatura que lo pise, tambien causa que estas criatura sigan esparciendo el deterioro por donde caminen. +spell.wizardry:decoy.desc=Crea un clon ilusorio del mago que atrae a los enemigos. El senuelo desaparecera en 30 segundos. +spell.wizardry:detonate.desc=Causa un explosion donde estes mirando, hiriendo a todas las criaturas cercanas - incluyendo al mago si se encuentra muy cerca. +spell.wizardry:diamondflesh.desc="Tus flechas no son nada contra mi!" +spell.wizardry:earthquake.desc=Un verdadero de Mago de Tierra puede mover montanas. +spell.wizardry:entrapment.desc=Atrpa al objetivo en una esfera de oscuridad que lo eleva y hiere continuamente. +spell.wizardry:fireball.desc=Lanza una bola de fuego en la direccion a la que apuntes. +spell.wizardry:firebolt.desc=Dispara un chorro de fuego a corta distancia en frente de ti. +spell.wizardry:firebomb.desc=Lanza una bomba de fuego, la cual detona cuando impacta quemando todo a su alrededor. +spell.wizardry:fire_resistance.desc=Concede resistencia al fuego al mago por 30 segundos. +spell.wizardry:fire_sigil.desc=Coloca una trampa de fuego que quema y hiere a la criatura que la pise. +spell.wizardry:fireskin.desc=Envuelve al mago en fuego por 30 segundos, provocando que cualquier que lo toque se queme. +spell.wizardry:firestorm.desc="Yo soy el dragon." +spell.wizardry:flame_ray.desc=Crea una corriente de llamas en la direccion a la que apuntas, la cual quema todo a su paso. +spell.wizardry:flaming_axe.desc=Crea una Hacha Llameante que quema a todos los enemigos que golpees. Dura 30 segundos. +spell.wizardry:flaming_weapon.desc=Imbuye con llamas la primera arma en el inventario del mago, la magia de deshace despues de 45 segundos. +spell.wizardry:flight.desc=Vuela como un aguila. +spell.wizardry:font_of_mana.desc="Fuimos llenados con una intensa energia magica que parecia emanar del centro del..." - Extraido del diario de un mago olvidado; el resto de la pagina esta quemada. +spell.wizardry:font_of_vitality.desc=Se siente increible. +spell.wizardry:force_arrow.desc=Dispara una flecha de fuerza en la direccion que apuntes. +spell.wizardry:forcefield.desc=Crea un campo de fuerza alrededor del mago que repele criaturas y proyectiles. +spell.wizardry:force_orb.desc=Lanza una esfera de fuerza que hiere y empuja criaturas cuando impacta. +spell.wizardry:forests_curse.desc="Como te atreves a entrar en mi bosque!" +spell.wizardry:freeze.desc=Congela al objetivo por 10 segundos. Tambien congela el agua y crea nieve en el suelo. +spell.wizardry:freezing_weapon.desc=Imbuye el poder del frio a la primera arma en el inventario del mago, provocando que el arma congela a sus objetivos. La magia deshace despues de 45 segundos. +spell.wizardry:frost_axe.desc=Crea un Hacha Escarchada la cual congela los enemigos que golpees. Dura 30 segundos. +spell.wizardry:frost_ray.desc=Crea un corriente de hielo en la direccion que apuntes la cual ralentiza y hiere continuamente al objetivo. +spell.wizardry:frost_sigil.desc=Coloca un trampa de hielo en el suelo el cual hiere y congela al cualquiera que la pise. +spell.wizardry:glide.desc=Permite que el mago planee en el aire. Debe mantenerse el boton presionado. +spell.wizardry:greater_fireball.desc=Lanza una bola de fuego de mayor tamano. +spell.wizardry:greater_heal.desc=Cura 4 corazones del mago. +spell.wizardry:group_heal.desc=Cura 3 corazones al mago, a aliados cercanos y a criaturas invocadas. +spell.wizardry:growth_aura.desc=Crece todos los cultivos cercanos al mago. Tambien hace crecer flores y hierba alta. +spell.wizardry:hailstorm.desc=Fue durante el Gran Invierno de la 3era Edad que los Magos de Hielo descubrieron su verdadero potencial. +spell.wizardry:heal.desc=Cura 2 corazones del usuario. +spell.wizardry:heal_ally.desc=Cura 2.5 corazones al objetivo. +spell.wizardry:healing_aura.desc=Crea una zona de energia curadora que regenera la salud de todos los alidados cercanos. Los No-_muertos dentro reciben dano. +spell.wizardry:homing_spark.desc=Crea una chispa flotante que persigue enemigos. +spell.wizardry:ice_age.desc="Seras congelado por toda la eternidad!" +spell.wizardry:ice_charge.desc=Lanza una carga de hielo que explota cuando impacta, congelando todas las criaturas y soltando fragmentos en todas las direcciones. +spell.wizardry:ice_lance.desc=Dispara una gran lanza de hielo en la direccion a la que estas apuntando, la cual atraviesa los objetivos hieriendo y congelandolos. +spell.wizardry:ice_shard.desc=Dispara un fragemento de hielo que hace dano y ralentiza +spell.wizardry:ice_shroud.desc=Cubre al mago con hielo, lo que ocasiona que cualquier que lo ataque sea congelado. +spell.wizardry:ice_spikes.desc=Causa que pinchos de hielo crezcan desde el suelo hiriendo a los enemigos. +spell.wizardry:ice_statue.desc=Congela al objetivo durante 20 segundos. El objetivo no se puede mover ni atacar pero tambien es inmune al dano. +spell.wizardry:ignite.desc=Quema al objetivo. Tambien funciona como un mechero. +spell.wizardry:imbue_weapon.desc=Imbuye la primera arma en el inventario con magia, haciendola mas efectiva. La magia se deshace despues de 45 segundos. +spell.wizardry:intimidate.desc=Emite un grunido intimidante el cual provoca que las criaturas espacen. +spell.wizardry:invigorating_presence.desc=Concede al mago y a los aliados cercanos fuerza incrementada por 45 segundos. +spell.wizardry:invisibility.desc=Le otorga invisibilidad al mago por 30 segundos. +spell.wizardry:invoke_weather.desc=Cambia el clima del mundo. +spell.wizardry:ironflesh.desc=Mejora grandemente la resistencia del mago por 30 segundos. +spell.wizardry:leap.desc=Causa que el mago salte alto y se mueve levemente hacia adelante. +spell.wizardry:levitation.desc=Levanta al mago hacia el cielo mientras el boton siga presionado. Si es usando antes de golpear el suelo, anula el dano por caida. +spell.wizardry:life_drain.desc=Crea una correinte de energia oscura que drena la vida de tu objetivo y te cura. +spell.wizardry:light.desc=Crea una esfera de luz que alumbra el area. Dura 30 segundos. +spell.wizardry:lightning_arrow.desc=Lanza una flecha electrificada en la direccion que apuntes. +spell.wizardry:lightning_bolt.desc=Causa que un trueno caiga en la direccion que apuntes. +spell.wizardry:lightning_disc.desc=Envia un disco electrico en la direccion que apuntes, el cual busca objetivos. +spell.wizardry:lightning_hammer.desc="Te golpeare con la ira de los cielos!" +spell.wizardry:lightning_pulse.desc=Carga el suelo cerca del mago con corriente electrica, hiriendo y repeliendo criaturas cercanas. +spell.wizardry:lightning_ray.desc=Crea una corriente de energia electrica que hiere a tus objetivos continuamente. +spell.wizardry:lightning_sigil.desc=Coloca una trampa electrificada que electrocuta a la criatura que la pise. +spell.wizardry:lightning_web.desc="Concentrare. Canaliza la tormenta en tu mente a traves de tu varita y liberala." +spell.wizardry:magic_missile.desc=Dispara una bala de energia magica. +spell.wizardry:metamorphosis.desc=Cambia la forma del objetivo. Solo funciona en algunas criaturas. +spell.wizardry:meteor.desc=Algunos magos solo quieren ver el mundo arder... +spell.wizardry:mind_control.desc=Toma control de la mente del objetivo por 30 segundos, provocando que cambie de bando y peleen a tu favor. No funciona con criaturas de voluntad muy fuerte. +spell.wizardry:mind_trick.desc=Confunde y desorienta al objetivo por 15 segundos, volviendolo incapaz de atacar. El efecto se desactiva si el objetivo recibe dano. +spell.wizardry:none.desc=Para obtener un libro de hechizo con el comando /give, usa la metadata: /give [player] wizardry:spell_book 1 [spell id] (_si encontraste este libro en un cofre significa que otro mod esta alterando las cosas). +spell.wizardry:oakflesh.desc=Mejora la resistencia del amgo durante 30 segundos. +spell.wizardry:petrify.desc=Convierte al objetivo en piedra hasta que se libere, con un chance de liberarse cuando se pone de noche. El objetivo no se puede mover ni atacar y no puede ser atacado. +spell.wizardry:phase_step.desc=Transporta al mago a traves de una pared de 1 bloque de grosor. El rango mejora el grosor por el cual te puedes transportar. +spell.wizardry:plague_of_darkness.desc=La Oscuridad los consumira a todos... +spell.wizardry:pocket_furnace.desc=Cocina 5 objetos en el inventario. Los objetos en la barra seran cocinados primero. +spell.wizardry:pocket_workbench.desc=Permite al mago craftear objetos. +spell.wizardry:poison.desc=Dispara veneno en la direccion que apuntes. +spell.wizardry:poison_bomb.desc=Lanza una bomba de veneno, la cual detona esparciendo mas veneno. +spell.wizardry:replenish_hunger.desc=Rellena 6 puntos de hambre del mago. +spell.wizardry:ring_of_fire.desc=Crea un anillo de fuego alrededor del mago, hiriendo y quemando a los objetivos cercanos. +spell.wizardry:shadow_ward.desc=Crea una pared de oscurdiad en frente del mago lo cual hace que la mitad del dano recibido sea aplicado al atacante. +spell.wizardry:shield.desc=Crea una barrera protectora que bloquea proyectiles y magia. Tambien concede al mago un efecto debil de resistencia. +spell.wizardry:shockwave.desc=Boom. +spell.wizardry:silverfish_swarm.desc="Ahhhh! Se estan multiplicando!!" +spell.wizardry:sixth_sense.desc=Le permite al mago sentir las criaturas cercanas a el, incluso a traves de paredes, durante 20 segundos. +spell.wizardry:slime.desc=Envuelve al objetivo en slime lo cual ralentiza y hiere continuamente. El slime explota despues de 10 segundos. +spell.wizardry:smoke_bomb.desc=Lanza una bomba de humo la cual explota cuando impacta. Creando una nube de humo y dandole ceguera a las criaturas en el area de impacto. +spell.wizardry:snare.desc=Coloca un trampa en el suelo que hiere y ralentiza al que la pise. +spell.wizardry:snowball.desc=Lanza una bola de nieve. +spell.wizardry:spark_bomb.desc=Lanza una bomba electrica que electrocuta a los objetivos cerca de su zona de impacto. +spell.wizardry:spectral_pathway.desc=Crea un puente magico indestructible al frente del mago que se extiende a 15 bloques. El puente desaperece luego de 60 segundos. +spell.wizardry:spider_swarm.desc=Invoca un emjambre de aranas venenosas. Desapareceran despues de 3o segundos o si son eliminadas. +spell.wizardry:static_aura.desc=Rodea al mago de electricidad durante 30 segundos, disparando chispa de rayo a cualquiera que lo golpee. +spell.wizardry:summon_blaze.desc=Invoca un Blaze que pelea por ti. El blaze desparecera despues de 30 segundos o si es derrotado. +spell.wizardry:summon_ice_giant.desc="Smash them!" +spell.wizardry:summon_ice_wraith.desc=Invoca un Espectro de Hielo que pelea por ti. El Espectro de Hielo desparecera despues de 30 segundos o si es derrotado. +spell.wizardry:summon_iron_golem.desc=Autómata autónomo automatizado automático. +spell.wizardry:summon_lightning_wraith.desc=Invoca un Espectro de Rayo que pelea por ti. El Espectro de Rayo desparecera despues de 30 segundos o si es derrotado. +spell.wizardry:summon_phoenix.desc=Desde las cenizas... +spell.wizardry:summon_shadow_wraith.desc=Invoca un Espectro de las Sombras que pelea por ti. +spell.wizardry:summon_skeleton.desc=Invoca un Esqueleto que pelea por ti. El esqueleto desparecera despues de 30 segundos o si es derrotado. +spell.wizardry:summon_skeleton_legion.desc="Levantate, Ejercito No-_muerto!" +spell.wizardry:summon_snow_golem.desc=Crea un Golem de Nieve que pelea por ti. Dura hasta que el golem muera. +spell.wizardry:summon_spirit_horse.desc=Invoca un Caballo Espectral para que lo montes. El caballo desparecera despues de que dure mucho tiempo sin ser montado o hagas shift-clic derecho con cualquier varita. +spell.wizardry:summon_spirit_wolf.desc=Invoca un Lobo Espiritual que pelea por ti. El lobo solo desparecera si es matado o haces shift-clic derecho con cualquier varita. +spell.wizardry:summon_storm_elemental.desc="Elemental de Rayo: Una manifestacion antigua de los elementos, difícilmente puede contener la energía que se agita dentro de el." - Guia del Mago sobre Entidades Arcanas, Volumen I_i +spell.wizardry:summon_wither_skeleton.desc=Invoca un Esqueleto del Wither que pelea por ti. El esqueleto del wither desparecera despues de 30 segundos o si es derrotado. +spell.wizardry:summon_zombie.desc=Invoca un Zombi que pelea por ti. El zombi desparecera despues de 30 segundos o si es derrotado. +spell.wizardry:telekinesis.desc=Mueve un item o un objeto pequeno hacia ti si haces clic derecho sobre el bloque. Tambien puede desarmar jugadores. +spell.wizardry:thunderbolt.desc=Dispara una bala de trueno que empuja los enemigos. +spell.wizardry:thunderstorm.desc="Mwahahahahahaha!" +spell.wizardry:tornado.desc=Libera un tornado en la direccion que mires, el cual atrae y levanta a todos las criaturas en su paso. +spell.wizardry:transience.desc=Hace que el mago transcienda, volviendo inmune a todo dano pero volviendo incapaz de atacar, interactuar o romper bloques +spell.wizardry:transportation.desc=Teletransporta al mago a su circulo de piedra. Para guardar un circulo de piedra haz clic derecho con el hechizo seleccionado. +spell.wizardry:vanishing_box.desc=Le permite al mago accesar a su cofre de Ender. +spell.wizardry:wall_of_frost.desc=El invierno en la punta de tus dedos. +spell.wizardry:water_breathing.desc=Le permite al mago respirar debajo del agua durante 60 segundos. +spell.wizardry:whirlwind.desc=Causa que el objetivo sea soplado lejos de ti a gran velocidad. +spell.wizardry:wither.desc=Dispara un rayo de oscuridad que aplica wither a todo lo que toque. +spell.wizardry:wither_skull.desc=Lanza una Cabeza de Wither en la direccion que apuntes. + +spell.wizardry:invoke_weather.sun=La lluvia empieza a parar... +spell.wizardry:invoke_weather.rain=El cielo se abre... +spell.wizardry:transportation.missing=Tu circulo de piedra no se encuentra o esta siendo obstruido... +spell.wizardry:transportation.undefined=Debes guardar un circulo de piedra primero! +spell.wizardry:transportation.wrongdimension=Tu circulo de piedra se encuentra en otra dimension... +spell.wizardry:clairvoyance.searching=Buscando... +spell.wizardry:clairvoyance.confirm=El camino revelado al lanzar %1$s ahora te llevara a este punto +spell.wizardry:clairvoyance.outofrange=Tu punto guardado esta muy lejos o es inaccesible... +spell.wizardry:clairvoyance.undefined=Debes guardar una localizacion primero! +spell.wizardry:clairvoyance.wrongdimension=Tu localizacion guardada esta en otra dimension... + +potion.wizardry:frost=Congelacion +potion.wizardry:fireskin=Piel de Fuego +potion.wizardry:ice_shroud=Manto de Hielo +potion.wizardry:static_aura=Aura Estatica +potion.wizardry:transience=Transitoriedad +potion.wizardry:decay=Deterioro +potion.wizardry:sixth_sense=Sexto Sentido +potion.wizardry:arcane_jammer=Perturbador Arcano +potion.wizardry:mind_trick=Truco Mental +potion.wizardry:mind_control=Control Mental +potion.wizardry:font_of_mana=Fuente de Mana +potion.wizardry:fear=Miedo + +enchantment.wizardry:magic_sword=Impregnacion +enchantment.wizardry:magic_bow=Impregnacion +enchantment.wizardry:flaming_weapon=Impregnacion de Fuego +enchantment.wizardry:freezing_weapon=Impregnacion de Fuego + +key.categories.wizardry=Wizardry + +key.wizardry:next_spell=Hechizo siguiente +key.wizardry:previous_spell=Hechizo anterior + +death.attack.wizardry_magic=%1$s fue matado por %2$s usando magia +death.attack.indirect_wizardry_magic=%1$s fue matado por %2$s usando magia + +commands.wizardry:cast.usage=/%1$s [player] [damage multiplier] [range multiplier] [duration multiplier] [blast multiplier] +commands.wizardry:cast.success=Lanzado con exito %1$s +commands.wizardry:cast.success_continuous=Lanzado con exito %1$s; repite el comando para parar +commands.wizardry:cast.success_remote=Lanzado con exito %1$s como %2$s +commands.wizardry:cast.success_remote_continuous=Lanzado con exito %1$s como %2$s; repite el comando para parar +commands.wizardry:cast.fail=Imposible lanzar %1$s +commands.wizardry:cast.not_found=No hay ningun hechizo con ID %1$s + +commands.wizardry:ally.usage=/%1$s [player] +commands.wizardry:ally.addally=%1$s ha sido anadido a la lista de aliados de %2$s +commands.wizardry:ally.removeally=%1$s ha sido removido de la lista de aliados de %2$s +commands.wizardry:ally.self=Los jugadores no pueden ser aliados de si mismos! +commands.wizardry:ally.permission=No tienes permiso para cambiar los aliados de otros jugadores + +commands.wizardry:allies.usage=/%1$s [player] +commands.wizardry:allies.list=Jugadores que son aliados: %1$s +commands.wizardry:allies.list_other=Jugadores que son aliados de %1$s: %2$s +commands.wizardry:allies.permission=No tienes permiso para ver los aliados de otros jugadores +commands.wizardry:allies.none=Ninguno + +commands.wizardry:discoverspell.usage=/%1$s [player] +commands.wizardry:discoverspell.not_found=There is no such spell with ID %1$s +commands.wizardry:discoverspell.clear=Cleared all spell discovery data for %1$s +commands.wizardry:discoverspell.all=Added all spells to %1$s's spell discovery data +commands.wizardry:discoverspell.addspell=Added %1$s to %2$s's spell discovery data +commands.wizardry:discoverspell.removespell=Removed %1$s from %2$s's spell discovery data + +config.wizardry.title.general=Mod Options + +config.wizardry.category.spells=Configurar Hechizos +config.wizardry.category.spells.tooltip=Seleccionar que hechizos estan activados +config.wizardry.title.spells=Configuracion de Hechizos +config.wizardry.subtitle.spells=Establece un hechizo en falso para desactivarlo. + +config.wizardry.category.resistances=Configurar Resistencias +config.wizardry.category.resistances.tooltip=Configura cuales mobs son inmunes a los tipos de magia +config.wizardry.title.resistances=Configuracion de Resistencias +config.wizardry.subtitle.resistances=Ve las descripciones de opciones individuales para mas detalles. + +config.wizardry.category.ids=Configurar IDs +config.wizardry.category.ids.tooltip=Cambiar los IDs usados por Wizardry +config.wizardry.title.ids=Configuracion de IDs +config.wizardry.subtitle.ids=Cambia estos IDs si tienen conflicto con otro mod. + +config.wizardry.tower_rarity=Rareza de Torres +config.wizardry.ore_dimensions=Dimensiones de Mena +config.wizardry.flower_dimensions=Dimensiones de Flores +config.wizardry.tower_dimensions=Dimensiones de Torres +config.wizardry.spell_book_drop_chance=Oportunidad de Dropeo de Libro de Hechizo +config.wizardry.generate_loot=Generar Botin +config.wizardry.firebomb_is_craftable=Bomba de Fuego es crafteable +config.wizardry.poison_bomb_is_craftable=Bomba de Veneno es crafteable +config.wizardry.smoke_bomb_is_craftable=Bomba de Humo es crafteable +config.wizardry.use_alternate_scroll_recipe=Usa la receta alternativa de pergamino +config.wizardry.teleport_through_unbreakable_blocks=Teletransportarte a traves de bloques indestructibles +config.wizardry.show_summoned_creature_names=Mostrar los nombres de las criaturas invocadas +config.wizardry.friendly_fire=Fuego Amigo +config.wizardry.telekinetic_disarmament=Desarme Telequinetico +config.wizardry.discovery_mode=Modo Descubrir +config.wizardry.enable_shift_scrolling=Permitir shift-rueda del raton para cambiar de hechizo +config.wizardry.minion_revenge_targeting=Enfoque de venganza de minion +config.wizardry.player_damage_scaling=Factor de escala de dano del Jugador +config.wizardry.npc_damage_scaling=Factor de escala de dano de NPC +config.wizardry.cast_command_multiplier_limit=Limite del Multiplicador de lanzamiento de hechizo por Comando +config.wizardry.summoned_creature_targets_whitelist=Lista blanca de objetivos de criaturas invocadas +config.wizardry.summoned_creature_targets_blacklist=Lista negra de objetivos de criaturas invocadas +config.wizardry.spell_hud_position=Posicion del HUD de hechizos +config.wizardry.cast_command_name=Nombre del Hechizo lanzado por comando +config.wizardry.discoverspell_command_name=Descubrir el nombre del hechizo lanzado por comando +config.wizardry.ally_command_name=Colocar el nombre del comando de aliado +config.wizardry.allies_command_name=Ver el nombre del comandod de aliado + +config.wizardry.mobs_immune_to_fire=Mobs Inmunes Al Fuego +config.wizardry.mobs_immune_to_ice=Mobs Inmunees Al Frio +config.wizardry.mobs_immune_to_lightning=Mobs Inmunes Al Rayo +config.wizardry.mobs_immune_to_wither=Mobs Inmunes Al Wither +config.wizardry.mobs_immune_to_poison=Mobs Inmunes Al Veneno + +config.wizardry.tower_rarity.tooltip=Rareza para las torres de magos. Numeros mayores suben la rareza. Coloque un 0 para desactivarlas. +config.wizardry.ore_dimensions.tooltip=Lista de dimensiones en las cuales la Mena de Cristal Magico aparecera. +config.wizardry.flower_dimensions.tooltip=Lista de dimensiones en las cuales apareceran flores de cristal magico. +config.wizardry.tower_dimensions.tooltip=Lista de dimensiones donde se generaran torres de magos. +config.wizardry.spell_book_drop_chance.tooltip=La probabilidad de que un mob arroje un libro de hechizos cuando muera. Mientras mas alto sea el numero, mas chance tendras. Coloca un 0 para desactivar el dropeo de libros. Coloque 200 para asegurar el dropeo 100%. +config.wizardry.generate_loot.tooltip=Si se debe generar botin del mod en cofres de mazmorras. +config.wizardry.firebomb_is_craftable.tooltip=Si las bombas de fuego son crafteables o no. +config.wizardry.poison_bomb_is_craftable.tooltip=Si las bombas de veneno son crafteables o no. +config.wizardry.smoke_bomb_is_craftable.tooltip=Si las bombas de humo son crafteables o no. +config.wizardry.use_alternate_scroll_recipe.tooltip=Si se requiere cristal magico para craftear pergaminos vacioes. Utilice esta opcion si otro mod tiene conflicto. +config.wizardry.teleport_through_unbreakable_blocks.tooltip=Si los jugadores son capaces de pasar a traves de bloques indestructibles (bedrock) usando Paso de Fase. +config.wizardry.show_summoned_creature_names.tooltip=Si mostrar los nombres de las criaturas invocadas y sus duenos sobre su cabeza. +config.wizardry.friendly_fire.tooltip=Permitir que los aliados se puedan herir con magia. +config.wizardry.telekinetic_disarmament.tooltip=Permitir que los jugadores puedan desarmar otros jugadores con el hechizo de Telequinesis. +config.wizardry.discovery_mode.tooltip=Para aquellos que le guste el misterio! Si esta en verdadero, los hechizos que no se hayan conjurado seran ilegibles. No tiene efecto en creativo. +config.wizardry.enable_shift_scrolling.tooltip=Si eres capaz de cambiar de hechizos al agacharte y usar la rueda del raton. Nota: Esta funcionalidad es del lado cliente, otros jugadores no dependen de esta, deben configurar su propia opcion. +config.wizardry.minion_revenge_targeting.tooltip=Si las criaturas invocadas atacan a su invocador si este los ataca. +config.wizardry.player_damage_scaling.tooltip=Factor de incremento de dano global para el dano hecho por jugadores, relativo a 1. +config.wizardry.npc_damage_scaling.tooltip=Factor de incremento de dano global para el dano hecho por NPCs, relativo a 1. +config.wizardry.cast_command_multiplier_limit.tooltip=Limite maximo usado con el comando /cast. Esto esta aqui para evitar que accidentalmente destruyan un mundo o servidor. +config.wizardry.summoned_creature_targets_whitelist.tooltip=Lista de nombres de entidades que los magos y criaturas invocadas pueden atacar. +config.wizardry.summoned_creature_targets_blacklist.tooltip=Lista de nombres de entidades que los magos y criaturas invocadas no pueden atacar. Si eliminas creeper hazlo bajo tu responsabilidad!! +config.wizardry.spell_hud_position.tooltip=La posicion de la interfaz de hechizos. +config.wizardry.cast_command_name.tooltip=El nombre del comando /cast. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'magia', entonces el comando seria /magia. +config.wizardry.discoverspell_command_name.tooltip=El nombre del comando /discoverspell. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'descubrir', entonces el comando seria /descubrir. +config.wizardry.ally_command_name.tooltip=El nombre del comando /ally. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'aliado', entonces el comando seria /aliado. +config.wizardry.allies_command_name.tooltip=El nombre del comando /allies command. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'aliados', entonces el comando seria /aliados. + +config.wizardry.mobs_immune_to_fire.tooltip=Lista de nombres de entidades que son inmunes al fuego, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al fuego. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). +config.wizardry.mobs_immune_to_ice.tooltip=Lista de nombres de entidades que son inmunes al frio, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al frio. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). +config.wizardry.mobs_immune_to_lightning.tooltip=Lista de nombres de entidades que son inmunes al rayo, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al rayo. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). +config.wizardry.mobs_immune_to_wither.tooltip=Lista de nombres de entidades que son inmunes al wither, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al wither. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). +config.wizardry.mobs_immune_to_poison.tooltip=Lista de nombres de entidades que son inmunes al veneno, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al veneno. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). + +wizard.debug=%1$s, %2$s, %3$s \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/lang/es_MX.lang b/src/main/resources/assets/wizardry/lang/es_MX.lang new file mode 100644 index 00000000..7608a50f --- /dev/null +++ b/src/main/resources/assets/wizardry/lang/es_MX.lang @@ -0,0 +1,736 @@ +tile.wizardry:arcane_workbench.name=Mesa de Trabajo Arcana +tile.wizardry:crystal_ore.name=Mena de Cristal Magico +tile.wizardry:petrified_stone.name=Piedra Petrificada +tile.wizardry:ice_statue.name=Estatua de Hielo +tile.wizardry:crystal_flower.name=Flor de Cristal Magico +tile.wizardry:snare.name=Trampa +tile.wizardry:transportation_stone.name=Piedra de Transportacion +tile.wizardry:spectral_block.name=Bloque Espectral +tile.wizardry:crystal_block.name=Bloque de Cristal Magico + +item.wizardry:magic_crystal.name=Cristal Magico +item.wizardry:magic_wand.name=Varita Magica +item.wizardry:apprentice_wand.name=Varita de Aprendiz +item.wizardry:advanced_wand.name=Varita Avanzada +item.wizardry:master_wand.name=Varita de Maestro +item.wizardry:spell_book.name=Libro de Hechizo + +item.wizardry:arcane_tome.name=Tomo de lo Arcano +item.wizardry:arcane_tome.desc1=Mejora cualquier %1$s +item.wizardry:arcane_tome.desc2=varita a nivel %1$s + +item.wizardry:wizard_handbook.name=El Manual del Hechizero +item.wizardry:wizard_handbook.desc=por %1$s + +item.wizardry:wand.buff=+%1$s %2$s de potencia +item.wizardry:wand.spell=Hechizo actual: %1$s +item.wizardry:wand.mana=Mana: %1$s/%2$s + +item.wizardry:wand.addally=%1$s ha sido anadido a tu lista de aliados +item.wizardry:wand.removeally=%1$s ha sido removido a tu lista de aliados + +item.wizardry:basic_fire_wand.name=Varita de Ascuas +item.wizardry:basic_ice_wand.name=Varita de Escarcha +item.wizardry:basic_lightning_wand.name=Varita de Chispas +item.wizardry:basic_necromancy_wand.name=Varita de las Sombras +item.wizardry:basic_earth_wand.name=Varita del Bosque +item.wizardry:basic_sorcery_wand.name=Varita del Misterio +item.wizardry:basic_healing_wand.name=Varita de la Curacion + +item.wizardry:apprentice_fire_wand.name=Varita del Piromano Aprendiz +item.wizardry:apprentice_ice_wand.name=Varita del Mago de Hielo Aprendiz +item.wizardry:apprentice_lightning_wand.name=Varita del Mago de Tormenta Aprendiz +item.wizardry:apprentice_necromancy_wand.name=Varita del Nigromante Aprendiz +item.wizardry:apprentice_earth_wand.name=Varita del Mago de Tierra Aprendiz +item.wizardry:apprentice_sorcery_wand.name=Varita del Hechicero Aprendiz +item.wizardry:apprentice_healing_wand.name=Varita del Sanador Aprendiz + +item.wizardry:advanced_fire_wand.name=Varita del Piromano +item.wizardry:advanced_ice_wand.name=Varita del Mago de Hielo +item.wizardry:advanced_lightning_wand.name=Varita del Mago de Tormenta +item.wizardry:advanced_necromancy_wand.name=Varita del Nigromante +item.wizardry:advanced_earth_wand.name=Varita del Mago de Tierra +item.wizardry:advanced_sorcery_wand.name=Varita del Hechicero +item.wizardry:advanced_healing_wand.name=Varita del Sanador + +item.wizardry:master_fire_wand.name=Varita del Piromano Maestro +item.wizardry:master_ice_wand.name=Varita del Mago de Hielo Maestro +item.wizardry:master_lightning_wand.name=Varita del Mago de Tormenta Maestro +item.wizardry:master_necromancy_wand.name=Varita del Nigromante Maestro +item.wizardry:master_earth_wand.name=Varita del Mago de Tierra Maestro +item.wizardry:master_sorcery_wand.name=Varita del Hechicero Maestro +item.wizardry:master_healing_wand.name=Varita del Sanador Maestro + +item.wizardry:spectral_sword.name=Espada Espectral +item.wizardry:spectral_pickaxe.name=Pico Espectral +item.wizardry:spectral_bow.name=Arco Espectral + +item.wizardry:mana_flask.name=Frasco de Mana +item.wizardry:storage_upgrade.name=Mejora de Almacenamiento para Varita +item.wizardry:siphon_upgrade.name=Mejora de Sifon para Varita +item.wizardry:condenser_upgrade.name=Mejora de Condensador para Varita +item.wizardry:range_upgrade.name=Mejora de Rango para Varita +item.wizardry:duration_upgrade.name=Mejora de Duracion para Varita +item.wizardry:cooldown_upgrade.name=Mejora de Tiempo de Reactivacion para Varita +item.wizardry:blast_upgrade.name=Mejora de Explosion para Varita +item.wizardry:attunement_upgrade.name=Mejora de Sintonizacion para Varita + +item.wizardry:flaming_axe.name=Hacha Llameante +item.wizardry:frost_axe.name=Hacha Escarchada + +item.wizardry:firebomb.name=Bomba de fuego +item.wizardry:poison_bomb.name=Bomba de veneno +item.wizardry:smoke_bomb.name=Bomba de humo + +item.wizardry:blank_scroll.name=Pergamino en blanco +item.wizardry:scroll.name=Pergamino de %1$s +item.wizardry:scroll.undiscovered.name=Pergamino "%1$s" +item.wizardry:identification_scroll.name=Pergamino de la Identificacion +item.wizardry:identification_scroll.desc1=%1$sIdentifica un libro o pergamino +item.wizardry:identification_scroll.desc2=%1$sdesconocido +item.wizardry:identification_scroll.nothing_to_identify=Nada que identificar! + +item.wizardry:armour_upgrade.name=Sello Arcano de la Proteccion +item.wizardry:armour_upgrade.desc1=%1$sMejora cualquier armadura +item.wizardry:armour_upgrade.desc2=%1$spara volverla %2$slegendaria + +item.wizardry:magic_silk.name=Seda Magica + +item.wizardry:wizard_armour.legendary=Legendario +item.wizardry:wizard_armour.buff=-%1$s %2$s costo +item.wizardry:wizard_armour.mana=Mana: %1$s/%2$s + +item.wizardry:wizard_hat.name=Sombrero de Mago +item.wizardry:wizard_robe.name=Tunica de Mago +item.wizardry:wizard_leggings.name=Pantalones de Mago +item.wizardry:wizard_boots.name=Botas de Mago + +item.wizardry:wizard_hat_fire.name=Sombrero de Piromano +item.wizardry:wizard_robe_fire.name=Tunica de Piromano +item.wizardry:wizard_leggings_fire.name=Pantalones de Piromano +item.wizardry:wizard_boots_fire.name=Botas de Piromano + +item.wizardry:wizard_hat_ice.name=Sombrero de Mago de Hielo +item.wizardry:wizard_robe_ice.name=Tunica de Mago de Hielo +item.wizardry:wizard_leggings_ice.name=Pantalones de Mago de Hielo +item.wizardry:wizard_boots_ice.name=Botas de Mago de Hielo + +item.wizardry:wizard_hat_lightning.name=Sombrero de Mago de Tormenta +item.wizardry:wizard_robe_lightning.name=Tunica de Mago de Tormenta +item.wizardry:wizard_leggings_lightning.name=Pantalones de Mago de Tormenta +item.wizardry:wizard_boots_lightning.name=Botas de Mago de Tormenta + +item.wizardry:wizard_hat_necromancy.name=Sombrero de Nigromante +item.wizardry:wizard_robe_necromancy.name=Tunica de Nigromante +item.wizardry:wizard_leggings_necromancy.name=Pantalones de Nigromante +item.wizardry:wizard_boots_necromancy.name=Botas de Nigromante + +item.wizardry:wizard_hat_earth.name=Sombrero de Mago de Tierra +item.wizardry:wizard_robe_earth.name=Tunica de Mago de Tierra +item.wizardry:wizard_leggings_earth.name=Pantalones de Mago de Tierra +item.wizardry:wizard_boots_earth.name=Botas de Mago de Tierra + +item.wizardry:wizard_hat_sorcery.name=Sombrero de Hechicero +item.wizardry:wizard_robe_sorcery.name=Tunica de Hechicero +item.wizardry:wizard_leggings_sorcery.name=Pantalones de Hechicero +item.wizardry:wizard_boots_sorcery.name=Botas de Hechicero + +item.wizardry:wizard_hat_healing.name=Sombrero de Sanador +item.wizardry:wizard_robe_healing.name=Tunica de Sanador +item.wizardry:wizard_leggings_healing.name=Pantalones de Sanador +item.wizardry:wizard_boots_healing.name=Botas de Sanador + +item.wizardry:spawn_wizard.name=Invocar Mago +item.wizardry:spawn_evil_wizard.name=Invocar Mago Malvado + +item.wizardry:spectral_helmet.name=Casco Espectral +item.wizardry:spectral_chestplate.name=Pechera Espectral +item.wizardry:spectral_leggings.name=Grebas Espectrales +item.wizardry:spectral_boots.name=Botas Espectrales + +entity.wizardry.summonedcreature.nameplate=%2$s de %1$s + +entity.wizardry.zombie_minion.name=Zombi +entity.wizardry.skeleton_minion.name=Esqueleto +entity.wizardry.spider_minion.name=Arana +entity.wizardry.blaze_minion.name=Blaze +entity.wizardry.ice_wraith.name=Espectro de Hielo +entity.wizardry.lightning_wraith.name=Espectro de Relampago +entity.wizardry.shadow_wraith.name=Espectro de las Sombras +entity.wizardry.spirit_wolf.name=Lobo Espiritual +entity.wizardry.spirit_horse.name=Caballo Espiritual +entity.wizardry.ice_giant.name=Gigante de Hielo +entity.wizardry.phoenix.name=Fenix +entity.wizardry.wizard.name=Mago +entity.wizardry.magic_slime.name=Slime Magico +entity.wizardry.silverfish_minion.name=Lepisma +entity.wizardry.storm_elemental.name=Elemental de Tormenta +entity.wizardry.evil_wizard.name=Mago Malvado +entity.wizardry.decoy.name=Senuelo + +entity.wizardry.magic_missile.name=Magia +entity.wizardry.arc.name=Magia +entity.wizardry.spark_bomb.name=Magia +entity.wizardry.ice_shard.name=Magia +entity.wizardry.firebomb.name=Magic +entity.wizardry.poison_bomb.name=Magia +entity.wizardry.force_orb.name=Magia +entity.wizardry.spark.name=Magia +entity.wizardry.darkness_orb.name=Magia +entity.wizardry.fire_sigil.name=Magia +entity.wizardry.frost_sigil.name=Magia +entity.wizardry.lightning_sigil.name=Magia +entity.wizardry.lightning_arrow.name=Magia +entity.wizardry.firebolt.name=Magia +entity.wizardry.ice_charge.name=Magia +entity.wizardry.force_arrow.name=Magia +entity.wizardry.dart.name=Magia +entity.wizardry.lightning_disc.name=Magia +entity.wizardry.thunderbolt.name=Magia +entity.wizardry.decay.name=Magia +entity.wizardry.ice_lance.name=Magia +entity.wizardry.smoke_bomb.name=Magia +entity.wizardry.ice_spike.name=Magia + +entity.wizardry.black_hole.name=Agujero Negro +entity.wizardry.shield.name=Escudo +entity.wizardry.meteor.name=Meteoro +entity.wizardry.blizzard.name=Ventisca +entity.wizardry.bubble.name=Burbuja +entity.wizardry.tornado.name=Tornado +entity.wizardry.lightning_hammer.name=Martillo Relampago +entity.wizardry.arrow_rain.name=Lluvia de Flechas +entity.wizardry.healing_aura.name=Aura Curador +entity.wizardry.forcefield.name=Campo de Fuerza +entity.wizardry.ring_of_fire.name=Anillo de Fuego +entity.wizardry.earthquake.name=Terremoto +entity.wizardry.falling_grass.name=Hierba Descendiente +entity.wizardry.hailstorm.name=Tormenta de Granizo +entity.wizardry.lightning_pulse.name=Pulso de Relampago + +item_group.wizardry=Wizardry +item_group.wizardryspells=Hechizos de Wizardry + +achievement.crystal=Un Cristral Curioso... +achievement.crystal.desc=Pica un Cristal Magico +achievement.arcane_initiate=Inicio Arcano +achievement.arcane_initiate.desc=Craftea una varita magica con una pepita de oro, un palo y un cristal magico +achievement.apprentice=Aprendiz de Mago +achievement.apprentice.desc=Usa un tomo de lo arcano para mejorar tu varita +achievement.master=Maestro Arcano +achievement.master.desc=Obten una varita de maestro +achievement.all_spells=Mago de Todos los Oficios +achievement.all_spells.desc=Lanza todos los hechizos disponibles +achievement.wizard_trade=Comercio Magico +achievement.wizard_trade.desc=Compra un objeto a un mago +achievement.buy_master_spell=El Conocimiento es Poder +achievement.buy_master_spell.wizardry:desc=Compra un hechizo maestro a un mago +achievement.freeze_blaze=Ya No Estas Tan Caliente +achievement.freeze_blaze.desc=Congela a un blaze +achievement.charge_creeper=Va a Explotar!! +achievement.charge_creeper.desc='Accidentalmente' carga un creeper +achievement.frankenstein=Frankenstein +achievement.frankenstein.desc=Convierte a un cerdo en un hombrecerdo zombi usando el hechizo de rayo +achievement.special_upgrade=Remiendo Arcano +achievement.special_upgrade.desc=Aplica una mejora a una varita +achievement.craft_flask=Es Magia, Embotellada! +achievement.craft_flask.desc=Craftea un frasco de mana +achievement.elemental=Elemental +achievement.elemental.desc=Obten una varita elemental +achievement.armour_set=Now Eres un Mago Apropiado +achievement.armour_set.desc=Craftea y equipa el set completo de armadura de mago +achievement.legendary=Legendario +achievement.legendary.desc=Obten una pieza de armadura de mago legendaria +achievement.self_destruct=Tiro por la culata +achievement.self_destruct.desc=Muere por tu propia magia +achievement.pig_tornado=No De Nuevo... +achievement.pig_tornado.desc=Monta un cerdo hacia un tornado +achievement.jam_wizard=Sesion de Interferencia +achievement.jam_wizard.desc=Usa el hechizo arcano de interferencia en un mago +achievement.slime_skeleton=Situacion Pegajosa +achievement.slime_skeleton.desc=Sumerge un esqueleto en slime +achievement.anger_wizard=Te Arrepentiras de Eso +achievement.anger_wizard.desc=Haz que un mago se enoje +achievement.defeat_evil_wizard=Justicia +achievement.defeat_evil_wizard.desc=Derrota a un mago malvado +achievement.max_out_wand=Equipada Al Maximo! +achievement.max_out_wand.desc=Aplica el maximo numero de mejoras a una varita maestra +achievement.element_master=Maestria Elemental +achievement.element_master.desc=Lanza todos los hechizos de cualquier elemento +achievement.identify_spell=Apreciacion Arcana +achievement.identify_spell.wizardry:desc=Usa un pergamino de la identificacion para identificar un libro de hechizos o pergamino + +tile.wizardry:transportation_stone.confirm=A partir de ahora retornaras a este lugar cuando lances %1$s +tile.wizardry:transportation_stone.invalid=Primero debes hacer un circulo con 8 piedras de transportacion! + +container.wizardry:arcane_workbench=Mesa de Trabajo Arcana +container.wizardry:arcane_workbench.apply=Aplicar +container.wizardry:arcane_workbench.mana=Mana: +container.wizardry:arcane_workbench.upgrades=Mejoras Aplicadas: + +tier.basic=Novato +tier.apprentice=Aprendiz +tier.advanced=Avanzado +tier.master=Maestro + +element.simple=Ninguno +element.fire=Fuego +element.ice=Hielo +element.lightning=Rayo +element.necromancy=Nigromancia +element.earth=Tierra +element.sorcery=Hechiceria +element.healing=Curacion + +element.simple.wizard=Mago +element.fire.wizard=Piromano +element.ice.wizard=Mago de Hielo +element.lightning.wizard=Mago de Tormenta +element.necromancy.wizard=Nigromante +element.earth.wizard=Mago de Tierra +element.sorcery.wizard=Hechicero +element.healing.wizard=Curador + +spelltype.attack=Ataque +spelltype.defence=Defensa +spelltype.utility=Utilidad +spelltype.minion=Secuaz + +spell.disabled=%1$s ha sido desactivado en la configuracion +spell.resist=%1$s resistio %2$s +spell.discover=Descubrio el hechizo %1$s! + +spell.wizardry:agility=Agilidad +spell.wizardry:arc=Arco Electrico +spell.wizardry:arcane_jammer=Perturbador Arcano +spell.wizardry:arrow_rain=Lluvia de Flechas +spell.wizardry:banish=Desaparecer +spell.wizardry:black_hole=Agujero Negro +spell.wizardry:blink=Parpadeo +spell.wizardry:blizzard=Ventisca +spell.wizardry:bubble=Burbuja +spell.wizardry:chain_lightning=Cadena de Rayos +spell.wizardry:clairvoyance=Clarividencia +spell.wizardry:cobwebs=Telas de Araña +spell.wizardry:conjure_armour=Conjurar Armadura +spell.wizardry:conjure_bow=Conjurar Arco +spell.wizardry:conjure_pickaxe=Conjurar Pico +spell.wizardry:conjure_sword=Conjurar Espada +spell.wizardry:cure_effects=Purificar Efectos +spell.wizardry:curse_of_soulbinding=Maldicion de Atadura al Alma +spell.wizardry:darkness_orb=Orbe de la Oscuridad +spell.wizardry:darkvision=Vision Oscura +spell.wizardry:dart=Dardo +spell.wizardry:decay=Deterioro +spell.wizardry:decoy=Senuelo +spell.wizardry:detonate=Detonar +spell.wizardry:diamondflesh=Piel de Diamante +spell.wizardry:earthquake=Terremoto +spell.wizardry:entrapment=Atrapamiento +spell.wizardry:fireball=Bola de Fuego +spell.wizardry:firebolt=Disparo de Fuego +spell.wizardry:firebomb=Bomba de Fuego +spell.wizardry:fire_resistance=Resistencia al Fuego +spell.wizardry:fire_sigil=Sigilo de Fuego +spell.wizardry:fireskin=Piel de Fuego +spell.wizardry:firestorm=Tormenta de Fuego +spell.wizardry:flame_ray=Rayo de Fuego +spell.wizardry:flaming_axe=Hacha Llameante +spell.wizardry:flaming_weapon=Arma Llameante +spell.wizardry:flight=Vuelo +spell.wizardry:font_of_mana=Fuente de Mana +spell.wizardry:font_of_vitality=Fuente de Vitalidad +spell.wizardry:force_arrow=Flecha de Fuerza +spell.wizardry:forcefield=Campo de Fuerza +spell.wizardry:force_orb=Orbe de Fuerza +spell.wizardry:forests_curse=Maldicion del Bosque +spell.wizardry:freeze=Congelar +spell.wizardry:freezing_weapon=Arma Congelante +spell.wizardry:frost_axe=Hacha Escarchada +spell.wizardry:frost_ray=Rayo de Escarcha +spell.wizardry:frost_sigil=Sigilo de Escarcha +spell.wizardry:glide=Planear +spell.wizardry:greater_fireball=Bola de Fuego Mayor +spell.wizardry:greater_heal=Curacion Mayor +spell.wizardry:group_heal=Curacion Grupal +spell.wizardry:growth_aura= Aura de Crecimiento +spell.wizardry:hailstorm=Tormenta de Granizo +spell.wizardry:heal=Curar +spell.wizardry:heal_ally=curar Aliado +spell.wizardry:healing_aura=Aura de Curacion +spell.wizardry:homing_spark=Chispa Dirigida +spell.wizardry:ice_age=Era de Hielo +spell.wizardry:ice_charge=Carga de Hielo +spell.wizardry:ice_lance=Lanza de Hielo +spell.wizardry:ice_shard=Fragmento de Hielo +spell.wizardry:ice_shroud=Velo de Hielo +spell.wizardry:ice_spikes=Pinchos de Hielo +spell.wizardry:ice_statue=Estatua de Hielo +spell.wizardry:ignite=Encender +spell.wizardry:imbue_weapon=Imbuir Arma +spell.wizardry:intimidate=Intimidar +spell.wizardry:invigorating_presence=Presencia Vigorizante +spell.wizardry:invisibility=Invisibilidad +spell.wizardry:invoke_weather=Invocar clima +spell.wizardry:ironflesh=Piel de Hierro +spell.wizardry:leap=Salto +spell.wizardry:levitation=Levitacion +spell.wizardry:life_drain=Drenaje de Vida +spell.wizardry:light=Luz +spell.wizardry:lightning_arrow=Flecha de Rayo +spell.wizardry:lightning_bolt=Disparo de Rayo +spell.wizardry:lightning_disc=Disco de Rayo +spell.wizardry:lightning_hammer=Martillo Relampago +spell.wizardry:lightning_pulse=Pulso Electrico +spell.wizardry:lightning_ray=Rayo Electrico +spell.wizardry:lightning_sigil=Sigilo Electrico +spell.wizardry:lightning_web=Red Electrica +spell.wizardry:magic_missile=Misil Magico +spell.wizardry:metamorphosis=Metamorfosis +spell.wizardry:meteor=Meteoro +spell.wizardry:mind_control=Control Mental +spell.wizardry:mind_trick=Truco Mental +spell.wizardry:none=[Espacio Vacio] +spell.wizardry:oakflesh=Piel de Roble +spell.wizardry:petrify=Petrificar +spell.wizardry:phase_step=Paso de Fase +spell.wizardry:plague_of_darkness=Plaga de la Oscuridad +spell.wizardry:pocket_furnace=Horno del Bolsillo +spell.wizardry:pocket_workbench=Mesa de Trabajo de Bolsillo +spell.wizardry:poison=Veneno +spell.wizardry:poison_bomb=Bomba de Veneno +spell.wizardry:replenish_hunger=Rellenar hambre +spell.wizardry:ring_of_fire=Anillo de Fuego +spell.wizardry:shadow_ward=Guarda de las Sombras +spell.wizardry:shield=Escudo +spell.wizardry:shockwave=Onda de Choque +spell.wizardry:silverfish_swarm=Emjambre de Lepismas +spell.wizardry:sixth_sense=Sexto Sentido +spell.wizardry:slime=Slime +spell.wizardry:smoke_bomb=Bomba de Humo +spell.wizardry:snare=Trampa +spell.wizardry:snowball=Bola de Nieve +spell.wizardry:spark_bomb=Bomba de Chispa +spell.wizardry:spectral_pathway=Camino Espectral +spell.wizardry:spider_swarm=Emjambre de Arañas +spell.wizardry:static_aura=Aura Estatica +spell.wizardry:summon_blaze=Invocar Blaze +spell.wizardry:summon_ice_giant=Invocar Gigante de Hielo +spell.wizardry:summon_ice_wraith=Invocar Espectro de Hielo +spell.wizardry:summon_iron_golem=Invocar Golem de Hierro +spell.wizardry:summon_lightning_wraith=Invocar Espectro de Rayo +spell.wizardry:summon_phoenix=Invocar Fenix +spell.wizardry:summon_shadow_wraith=Invocar Espectro de las Sombras +spell.wizardry:summon_skeleton=Invocar Esqueleto +spell.wizardry:summon_skeleton_legion=Invocar Legion de Esqueletos +spell.wizardry:summon_snow_golem=Invocar Golem de Nieve +spell.wizardry:summon_spirit_horse=Invocar Caballo Espiritual +spell.wizardry:summon_spirit_wolf=Invocar Lobo Espiritual +spell.wizardry:summon_storm_elemental=Invocar Elemental del Rayo +spell.wizardry:summon_wither_skeleton=Invocar Esqueleto del Wither +spell.wizardry:summon_zombie=Invocar Zombi +spell.wizardry:telekinesis=Telequinesis +spell.wizardry:thunderbolt=Rayo +spell.wizardry:thunderstorm=Tormenta Electrica +spell.wizardry:tornado=Tornado +spell.wizardry:transience=Transitoriedad +spell.wizardry:transportation=Transportacion +spell.wizardry:vanishing_box=Caja Desvaneciente +spell.wizardry:wall_of_frost=Pared de Escarcha +spell.wizardry:water_breathing=Respiracion Acuatica +spell.wizardry:whirlwind=Remolino de Viento +spell.wizardry:wither=Marchitar +spell.wizardry:wither_skull=Craneo de Wither + +spell.wizardry:agility.desc=Concede al mago velocidad de movimiento incrementada y un salto mas alto por 30 segundos. +spell.wizardry:arc.desc=Dispara una chispa de rayo al objetivo. +spell.wizardry:arcane_jammer.desc=No permite que el usuario use magia en los proximos 15 segundos. +spell.wizardry:arrow_rain.desc="Arqueros, disparen!" +spell.wizardry:banish.desc=Teletransporta al objetivo una posicion cercana aleatoria en contra de su voluntad. +spell.wizardry:black_hole.desc=Desgarra la realidad. +spell.wizardry:blink.desc=Teletransporta al mago a la posicion que estan apuntando. +spell.wizardry:blizzard.desc=Crea una zona de aire frio, la cual ralentiza y hiere continuamente a cualquiera en su interior. El creador del hechizo es inmune al dano pero no al efecto. +spell.wizardry:bubble.desc=Dispara un chorro de burbujas que causa que lo primero que toque flote hacia el cielo. La burbuja explota despues de un tiempo o si el objetivo es golpeado. +spell.wizardry:chain_lightning.desc=Dispara un relampago a un objetivo, el cual hace cadena con objetivos cercanos hasta dos veces. +spell.wizardry:clairvoyance.desc=Revela el camino hacia una localizacion guardada. Con este hechizo seleccionado, haga shift-clic derecho en un bloque para guardarlo. Lanza este hechizo para revelar el camino. El camino desaparecera despues de 90 segundos. +spell.wizardry:cobwebs.desc=Crea telas de arana en la direccion a la cual estas apuntando, lo cual limita la movilidad de tu objetivo. Las telas de arana despareceran en 20 segundos o si son rotas. +spell.wizardry:conjure_armour.desc=Crea Armadura Espectral alrededor del mago lo cual ofrece proteccion similar a la armadura de hierro. La armadura dura 60 segundos. El mago debe tener el espacio de armadura vacio. +spell.wizardry:conjure_bow.desc=Crea un Arco Espectral con flechas ilimitadas que dura 30 segundos. +spell.wizardry:conjure_pickaxe.desc=Crea un Pico Espectral con fuerza igual a la de un Pico de Hierro, el cual dura 30 segundos. +spell.wizardry:conjure_sword.desc=Crea una Espada Espectral con igual fuerza que una de hierro y dura 30 segundos. +spell.wizardry:cure_effects.desc=Purifica al usuario de todos los efectos de pocion, ya sean buenos o malos. +spell.wizardry:curse_of_soulbinding.desc=Provoca que el alma del objetivo esta ligada al del mago, lo que significa que todo el dano que recibe el mago lo recibe el objetivo. La maldicion dura hasta que uno de los dos muera. +spell.wizardry:darkness_orb.desc=Dispara una bala de energia oscura en la direccion que apuntes, la cual aplicar wither. +spell.wizardry:darkvision.desc=Concede vision nocturna por 45 segundos. +spell.wizardry:dart.desc=Dispara un dardo en la direccion que estas apuntando el cual hiere y debilita al objetivo. +spell.wizardry:decay.desc=Crea un pezado de deterioro en el suelo lo cual infecta y hiere a cualquier criatura que lo pise, tambien causa que estas criatura sigan esparciendo el deterioro por donde caminen. +spell.wizardry:decoy.desc=Crea un clon ilusorio del mago que atrae a los enemigos. El senuelo desaparecera en 30 segundos. +spell.wizardry:detonate.desc=Causa un explosion donde estes mirando, hiriendo a todas las criaturas cercanas - incluyendo al mago si se encuentra muy cerca. +spell.wizardry:diamondflesh.desc="Tus flechas no son nada contra mi!" +spell.wizardry:earthquake.desc=Un verdadero de Mago de Tierra puede mover montanas. +spell.wizardry:entrapment.desc=Atrpa al objetivo en una esfera de oscuridad que lo eleva y hiere continuamente. +spell.wizardry:fireball.desc=Lanza una bola de fuego en la direccion a la que apuntes. +spell.wizardry:firebolt.desc=Dispara un chorro de fuego a corta distancia en frente de ti. +spell.wizardry:firebomb.desc=Lanza una bomba de fuego, la cual detona cuando impacta quemando todo a su alrededor. +spell.wizardry:fire_resistance.desc=Concede resistencia al fuego al mago por 30 segundos. +spell.wizardry:fire_sigil.desc=Coloca una trampa de fuego que quema y hiere a la criatura que la pise. +spell.wizardry:fireskin.desc=Envuelve al mago en fuego por 30 segundos, provocando que cualquier que lo toque se queme. +spell.wizardry:firestorm.desc="Yo soy el dragon." +spell.wizardry:flame_ray.desc=Crea una corriente de llamas en la direccion a la que apuntas, la cual quema todo a su paso. +spell.wizardry:flaming_axe.desc=Crea una Hacha Llameante que quema a todos los enemigos que golpees. Dura 30 segundos. +spell.wizardry:flaming_weapon.desc=Imbuye con llamas la primera arma en el inventario del mago, la magia de deshace despues de 45 segundos. +spell.wizardry:flight.desc=Vuela como un aguila. +spell.wizardry:font_of_mana.desc="Fuimos llenados con una intensa energia magica que parecia emanar del centro del..." - Extraido del diario de un mago olvidado; el resto de la pagina esta quemada. +spell.wizardry:font_of_vitality.desc=Se siente increible. +spell.wizardry:force_arrow.desc=Dispara una flecha de fuerza en la direccion que apuntes. +spell.wizardry:forcefield.desc=Crea un campo de fuerza alrededor del mago que repele criaturas y proyectiles. +spell.wizardry:force_orb.desc=Lanza una esfera de fuerza que hiere y empuja criaturas cuando impacta. +spell.wizardry:forests_curse.desc="Como te atreves a entrar en mi bosque!" +spell.wizardry:freeze.desc=Congela al objetivo por 10 segundos. Tambien congela el agua y crea nieve en el suelo. +spell.wizardry:freezing_weapon.desc=Imbuye el poder del frio a la primera arma en el inventario del mago, provocando que el arma congela a sus objetivos. La magia deshace despues de 45 segundos. +spell.wizardry:frost_axe.desc=Crea un Hacha Escarchada la cual congela los enemigos que golpees. Dura 30 segundos. +spell.wizardry:frost_ray.desc=Crea un corriente de hielo en la direccion que apuntes la cual ralentiza y hiere continuamente al objetivo. +spell.wizardry:frost_sigil.desc=Coloca un trampa de hielo en el suelo el cual hiere y congela al cualquiera que la pise. +spell.wizardry:glide.desc=Permite que el mago planee en el aire. Debe mantenerse el boton presionado. +spell.wizardry:greater_fireball.desc=Lanza una bola de fuego de mayor tamano. +spell.wizardry:greater_heal.desc=Cura 4 corazones del mago. +spell.wizardry:group_heal.desc=Cura 3 corazones al mago, a aliados cercanos y a criaturas invocadas. +spell.wizardry:growth_aura.desc=Crece todos los cultivos cercanos al mago. Tambien hace crecer flores y hierba alta. +spell.wizardry:hailstorm.desc=Fue durante el Gran Invierno de la 3era Edad que los Magos de Hielo descubrieron su verdadero potencial. +spell.wizardry:heal.desc=Cura 2 corazones del usuario. +spell.wizardry:heal_ally.desc=Cura 2.5 corazones al objetivo. +spell.wizardry:healing_aura.desc=Crea una zona de energia curadora que regenera la salud de todos los alidados cercanos. Los No-_muertos dentro reciben dano. +spell.wizardry:homing_spark.desc=Crea una chispa flotante que persigue enemigos. +spell.wizardry:ice_age.desc="Seras congelado por toda la eternidad!" +spell.wizardry:ice_charge.desc=Lanza una carga de hielo que explota cuando impacta, congelando todas las criaturas y soltando fragmentos en todas las direcciones. +spell.wizardry:ice_lance.desc=Dispara una gran lanza de hielo en la direccion a la que estas apuntando, la cual atraviesa los objetivos hieriendo y congelandolos. +spell.wizardry:ice_shard.desc=Dispara un fragemento de hielo que hace dano y ralentiza +spell.wizardry:ice_shroud.desc=Cubre al mago con hielo, lo que ocasiona que cualquier que lo ataque sea congelado. +spell.wizardry:ice_spikes.desc=Causa que pinchos de hielo crezcan desde el suelo hiriendo a los enemigos. +spell.wizardry:ice_statue.desc=Congela al objetivo durante 20 segundos. El objetivo no se puede mover ni atacar pero tambien es inmune al dano. +spell.wizardry:ignite.desc=Quema al objetivo. Tambien funciona como un mechero. +spell.wizardry:imbue_weapon.desc=Imbuye la primera arma en el inventario con magia, haciendola mas efectiva. La magia se deshace despues de 45 segundos. +spell.wizardry:intimidate.desc=Emite un grunido intimidante el cual provoca que las criaturas espacen. +spell.wizardry:invigorating_presence.desc=Concede al mago y a los aliados cercanos fuerza incrementada por 45 segundos. +spell.wizardry:invisibility.desc=Le otorga invisibilidad al mago por 30 segundos. +spell.wizardry:invoke_weather.desc=Cambia el clima del mundo. +spell.wizardry:ironflesh.desc=Mejora grandemente la resistencia del mago por 30 segundos. +spell.wizardry:leap.desc=Causa que el mago salte alto y se mueve levemente hacia adelante. +spell.wizardry:levitation.desc=Levanta al mago hacia el cielo mientras el boton siga presionado. Si es usando antes de golpear el suelo, anula el dano por caida. +spell.wizardry:life_drain.desc=Crea una correinte de energia oscura que drena la vida de tu objetivo y te cura. +spell.wizardry:light.desc=Crea una esfera de luz que alumbra el area. Dura 30 segundos. +spell.wizardry:lightning_arrow.desc=Lanza una flecha electrificada en la direccion que apuntes. +spell.wizardry:lightning_bolt.desc=Causa que un trueno caiga en la direccion que apuntes. +spell.wizardry:lightning_disc.desc=Envia un disco electrico en la direccion que apuntes, el cual busca objetivos. +spell.wizardry:lightning_hammer.desc="Te golpeare con la ira de los cielos!" +spell.wizardry:lightning_pulse.desc=Carga el suelo cerca del mago con corriente electrica, hiriendo y repeliendo criaturas cercanas. +spell.wizardry:lightning_ray.desc=Crea una corriente de energia electrica que hiere a tus objetivos continuamente. +spell.wizardry:lightning_sigil.desc=Coloca una trampa electrificada que electrocuta a la criatura que la pise. +spell.wizardry:lightning_web.desc="Concentrare. Canaliza la tormenta en tu mente a traves de tu varita y liberala." +spell.wizardry:magic_missile.desc=Dispara una bala de energia magica. +spell.wizardry:metamorphosis.desc=Cambia la forma del objetivo. Solo funciona en algunas criaturas. +spell.wizardry:meteor.desc=Algunos magos solo quieren ver el mundo arder... +spell.wizardry:mind_control.desc=Toma control de la mente del objetivo por 30 segundos, provocando que cambie de bando y peleen a tu favor. No funciona con criaturas de voluntad muy fuerte. +spell.wizardry:mind_trick.desc=Confunde y desorienta al objetivo por 15 segundos, volviendolo incapaz de atacar. El efecto se desactiva si el objetivo recibe dano. +spell.wizardry:none.desc=Para obtener un libro de hechizo con el comando /give, usa la metadata: /give [player] wizardry:spell_book 1 [spell id] (_si encontraste este libro en un cofre significa que otro mod esta alterando las cosas). +spell.wizardry:oakflesh.desc=Mejora la resistencia del amgo durante 30 segundos. +spell.wizardry:petrify.desc=Convierte al objetivo en piedra hasta que se libere, con un chance de liberarse cuando se pone de noche. El objetivo no se puede mover ni atacar y no puede ser atacado. +spell.wizardry:phase_step.desc=Transporta al mago a traves de una pared de 1 bloque de grosor. El rango mejora el grosor por el cual te puedes transportar. +spell.wizardry:plague_of_darkness.desc=La Oscuridad los consumira a todos... +spell.wizardry:pocket_furnace.desc=Cocina 5 objetos en el inventario. Los objetos en la barra seran cocinados primero. +spell.wizardry:pocket_workbench.desc=Permite al mago craftear objetos. +spell.wizardry:poison.desc=Dispara veneno en la direccion que apuntes. +spell.wizardry:poison_bomb.desc=Lanza una bomba de veneno, la cual detona esparciendo mas veneno. +spell.wizardry:replenish_hunger.desc=Rellena 6 puntos de hambre del mago. +spell.wizardry:ring_of_fire.desc=Crea un anillo de fuego alrededor del mago, hiriendo y quemando a los objetivos cercanos. +spell.wizardry:shadow_ward.desc=Crea una pared de oscurdiad en frente del mago lo cual hace que la mitad del dano recibido sea aplicado al atacante. +spell.wizardry:shield.desc=Crea una barrera protectora que bloquea proyectiles y magia. Tambien concede al mago un efecto debil de resistencia. +spell.wizardry:shockwave.desc=Boom. +spell.wizardry:silverfish_swarm.desc="Ahhhh! Se estan multiplicando!!" +spell.wizardry:sixth_sense.desc=Le permite al mago sentir las criaturas cercanas a el, incluso a traves de paredes, durante 20 segundos. +spell.wizardry:slime.desc=Envuelve al objetivo en slime lo cual ralentiza y hiere continuamente. El slime explota despues de 10 segundos. +spell.wizardry:smoke_bomb.desc=Lanza una bomba de humo la cual explota cuando impacta. Creando una nube de humo y dandole ceguera a las criaturas en el area de impacto. +spell.wizardry:snare.desc=Coloca un trampa en el suelo que hiere y ralentiza al que la pise. +spell.wizardry:snowball.desc=Lanza una bola de nieve. +spell.wizardry:spark_bomb.desc=Lanza una bomba electrica que electrocuta a los objetivos cerca de su zona de impacto. +spell.wizardry:spectral_pathway.desc=Crea un puente magico indestructible al frente del mago que se extiende a 15 bloques. El puente desaperece luego de 60 segundos. +spell.wizardry:spider_swarm.desc=Invoca un emjambre de aranas venenosas. Desapareceran despues de 3o segundos o si son eliminadas. +spell.wizardry:static_aura.desc=Rodea al mago de electricidad durante 30 segundos, disparando chispa de rayo a cualquiera que lo golpee. +spell.wizardry:summon_blaze.desc=Invoca un Blaze que pelea por ti. El blaze desparecera despues de 30 segundos o si es derrotado. +spell.wizardry:summon_ice_giant.desc="Smash them!" +spell.wizardry:summon_ice_wraith.desc=Invoca un Espectro de Hielo que pelea por ti. El Espectro de Hielo desparecera despues de 30 segundos o si es derrotado. +spell.wizardry:summon_iron_golem.desc=Autómata autónomo automatizado automático. +spell.wizardry:summon_lightning_wraith.desc=Invoca un Espectro de Rayo que pelea por ti. El Espectro de Rayo desparecera despues de 30 segundos o si es derrotado. +spell.wizardry:summon_phoenix.desc=Desde las cenizas... +spell.wizardry:summon_shadow_wraith.desc=Invoca un Espectro de las Sombras que pelea por ti. +spell.wizardry:summon_skeleton.desc=Invoca un Esqueleto que pelea por ti. El esqueleto desparecera despues de 30 segundos o si es derrotado. +spell.wizardry:summon_skeleton_legion.desc="Levantate, Ejercito No-_muerto!" +spell.wizardry:summon_snow_golem.desc=Crea un Golem de Nieve que pelea por ti. Dura hasta que el golem muera. +spell.wizardry:summon_spirit_horse.desc=Invoca un Caballo Espectral para que lo montes. El caballo desparecera despues de que dure mucho tiempo sin ser montado o hagas shift-clic derecho con cualquier varita. +spell.wizardry:summon_spirit_wolf.desc=Invoca un Lobo Espiritual que pelea por ti. El lobo solo desparecera si es matado o haces shift-clic derecho con cualquier varita. +spell.wizardry:summon_storm_elemental.desc="Elemental de Rayo: Una manifestacion antigua de los elementos, difícilmente puede contener la energía que se agita dentro de el." - Guia del Mago sobre Entidades Arcanas, Volumen I_i +spell.wizardry:summon_wither_skeleton.desc=Invoca un Esqueleto del Wither que pelea por ti. El esqueleto del wither desparecera despues de 30 segundos o si es derrotado. +spell.wizardry:summon_zombie.desc=Invoca un Zombi que pelea por ti. El zombi desparecera despues de 30 segundos o si es derrotado. +spell.wizardry:telekinesis.desc=Mueve un item o un objeto pequeno hacia ti si haces clic derecho sobre el bloque. Tambien puede desarmar jugadores. +spell.wizardry:thunderbolt.desc=Dispara una bala de trueno que empuja los enemigos. +spell.wizardry:thunderstorm.desc="Mwahahahahahaha!" +spell.wizardry:tornado.desc=Libera un tornado en la direccion que mires, el cual atrae y levanta a todos las criaturas en su paso. +spell.wizardry:transience.desc=Hace que el mago transcienda, volviendo inmune a todo dano pero volviendo incapaz de atacar, interactuar o romper bloques +spell.wizardry:transportation.desc=Teletransporta al mago a su circulo de piedra. Para guardar un circulo de piedra haz clic derecho con el hechizo seleccionado. +spell.wizardry:vanishing_box.desc=Le permite al mago accesar a su cofre de Ender. +spell.wizardry:wall_of_frost.desc=El invierno en la punta de tus dedos. +spell.wizardry:water_breathing.desc=Le permite al mago respirar debajo del agua durante 60 segundos. +spell.wizardry:whirlwind.desc=Causa que el objetivo sea soplado lejos de ti a gran velocidad. +spell.wizardry:wither.desc=Dispara un rayo de oscuridad que aplica wither a todo lo que toque. +spell.wizardry:wither_skull.desc=Lanza una Cabeza de Wither en la direccion que apuntes. + +spell.wizardry:invoke_weather.sun=La lluvia empieza a parar... +spell.wizardry:invoke_weather.rain=El cielo se abre... +spell.wizardry:transportation.missing=Tu circulo de piedra no se encuentra o esta siendo obstruido... +spell.wizardry:transportation.undefined=Debes guardar un circulo de piedra primero! +spell.wizardry:transportation.wrongdimension=Tu circulo de piedra se encuentra en otra dimension... +spell.wizardry:clairvoyance.searching=Buscando... +spell.wizardry:clairvoyance.confirm=El camino revelado al lanzar %1$s ahora te llevara a este punto +spell.wizardry:clairvoyance.outofrange=Tu punto guardado esta muy lejos o es inaccesible... +spell.wizardry:clairvoyance.undefined=Debes guardar una localizacion primero! +spell.wizardry:clairvoyance.wrongdimension=Tu localizacion guardada esta en otra dimension... + +potion.wizardry:frost=Congelacion +potion.wizardry:fireskin=Piel de Fuego +potion.wizardry:ice_shroud=Manto de Hielo +potion.wizardry:static_aura=Aura Estatica +potion.wizardry:transience=Transitoriedad +potion.wizardry:decay=Deterioro +potion.wizardry:sixth_sense=Sexto Sentido +potion.wizardry:arcane_jammer=Perturbador Arcano +potion.wizardry:mind_trick=Truco Mental +potion.wizardry:mind_control=Control Mental +potion.wizardry:font_of_mana=Fuente de Mana +potion.wizardry:fear=Miedo + +enchantment.wizardry:magic_sword=Impregnacion +enchantment.wizardry:magic_bow=Impregnacion +enchantment.wizardry:flaming_weapon=Impregnacion de Fuego +enchantment.wizardry:freezing_weapon=Impregnacion de Fuego + +key.categories.wizardry=Wizardry + +key.wizardry:next_spell=Hechizo siguiente +key.wizardry:previous_spell=Hechizo anterior + +death.attack.wizardry_magic=%1$s fue matado por %2$s usando magia +death.attack.indirect_wizardry_magic=%1$s fue matado por %2$s usando magia + +commands.wizardry:cast.usage=/%1$s [player] [damage multiplier] [range multiplier] [duration multiplier] [blast multiplier] +commands.wizardry:cast.success=Lanzado con exito %1$s +commands.wizardry:cast.success_continuous=Lanzado con exito %1$s; repite el comando para parar +commands.wizardry:cast.success_remote=Lanzado con exito %1$s como %2$s +commands.wizardry:cast.success_remote_continuous=Lanzado con exito %1$s como %2$s; repite el comando para parar +commands.wizardry:cast.fail=Imposible lanzar %1$s +commands.wizardry:cast.not_found=No hay ningun hechizo con ID %1$s + +commands.wizardry:ally.usage=/%1$s [player] +commands.wizardry:ally.addally=%1$s ha sido anadido a la lista de aliados de %2$s +commands.wizardry:ally.removeally=%1$s ha sido removido de la lista de aliados de %2$s +commands.wizardry:ally.self=Los jugadores no pueden ser aliados de si mismos! +commands.wizardry:ally.permission=No tienes permiso para cambiar los aliados de otros jugadores + +commands.wizardry:allies.usage=/%1$s [player] +commands.wizardry:allies.list=Jugadores que son aliados: %1$s +commands.wizardry:allies.list_other=Jugadores que son aliados de %1$s: %2$s +commands.wizardry:allies.permission=No tienes permiso para ver los aliados de otros jugadores +commands.wizardry:allies.none=Ninguno + +commands.wizardry:discoverspell.usage=/%1$s [player] +commands.wizardry:discoverspell.not_found=There is no such spell with ID %1$s +commands.wizardry:discoverspell.clear=Cleared all spell discovery data for %1$s +commands.wizardry:discoverspell.all=Added all spells to %1$s's spell discovery data +commands.wizardry:discoverspell.addspell=Added %1$s to %2$s's spell discovery data +commands.wizardry:discoverspell.removespell=Removed %1$s from %2$s's spell discovery data + +config.wizardry.title.general=Mod Options + +config.wizardry.category.spells=Configurar Hechizos +config.wizardry.category.spells.tooltip=Seleccionar que hechizos estan activados +config.wizardry.title.spells=Configuracion de Hechizos +config.wizardry.subtitle.spells=Establece un hechizo en falso para desactivarlo. + +config.wizardry.category.resistances=Configurar Resistencias +config.wizardry.category.resistances.tooltip=Configura cuales mobs son inmunes a los tipos de magia +config.wizardry.title.resistances=Configuracion de Resistencias +config.wizardry.subtitle.resistances=Ve las descripciones de opciones individuales para mas detalles. + +config.wizardry.category.ids=Configurar IDs +config.wizardry.category.ids.tooltip=Cambiar los IDs usados por Wizardry +config.wizardry.title.ids=Configuracion de IDs +config.wizardry.subtitle.ids=Cambia estos IDs si tienen conflicto con otro mod. + +config.wizardry.tower_rarity=Rareza de Torres +config.wizardry.ore_dimensions=Dimensiones de Mena +config.wizardry.flower_dimensions=Dimensiones de Flores +config.wizardry.tower_dimensions=Dimensiones de Torres +config.wizardry.spell_book_drop_chance=Oportunidad de Dropeo de Libro de Hechizo +config.wizardry.generate_loot=Generar Botin +config.wizardry.firebomb_is_craftable=Bomba de Fuego es crafteable +config.wizardry.poison_bomb_is_craftable=Bomba de Veneno es crafteable +config.wizardry.smoke_bomb_is_craftable=Bomba de Humo es crafteable +config.wizardry.use_alternate_scroll_recipe=Usa la receta alternativa de pergamino +config.wizardry.teleport_through_unbreakable_blocks=Teletransportarte a traves de bloques indestructibles +config.wizardry.show_summoned_creature_names=Mostrar los nombres de las criaturas invocadas +config.wizardry.friendly_fire=Fuego Amigo +config.wizardry.telekinetic_disarmament=Desarme Telequinetico +config.wizardry.discovery_mode=Modo Descubrir +config.wizardry.enable_shift_scrolling=Permitir shift-rueda del raton para cambiar de hechizo +config.wizardry.minion_revenge_targeting=Enfoque de venganza de minion +config.wizardry.player_damage_scaling=Factor de escala de dano del Jugador +config.wizardry.npc_damage_scaling=Factor de escala de dano de NPC +config.wizardry.cast_command_multiplier_limit=Limite del Multiplicador de lanzamiento de hechizo por Comando +config.wizardry.summoned_creature_targets_whitelist=Lista blanca de objetivos de criaturas invocadas +config.wizardry.summoned_creature_targets_blacklist=Lista negra de objetivos de criaturas invocadas +config.wizardry.spell_hud_position=Posicion del HUD de hechizos +config.wizardry.cast_command_name=Nombre del Hechizo lanzado por comando +config.wizardry.discoverspell_command_name=Descubrir el nombre del hechizo lanzado por comando +config.wizardry.ally_command_name=Colocar el nombre del comando de aliado +config.wizardry.allies_command_name=Ver el nombre del comandod de aliado + +config.wizardry.mobs_immune_to_fire=Mobs Inmunes Al Fuego +config.wizardry.mobs_immune_to_ice=Mobs Inmunees Al Frio +config.wizardry.mobs_immune_to_lightning=Mobs Inmunes Al Rayo +config.wizardry.mobs_immune_to_wither=Mobs Inmunes Al Wither +config.wizardry.mobs_immune_to_poison=Mobs Inmunes Al Veneno + +config.wizardry.tower_rarity.tooltip=Rareza para las torres de magos. Numeros mayores suben la rareza. Coloque un 0 para desactivarlas. +config.wizardry.ore_dimensions.tooltip=Lista de dimensiones en las cuales la Mena de Cristal Magico aparecera. +config.wizardry.flower_dimensions.tooltip=Lista de dimensiones en las cuales apareceran flores de cristal magico. +config.wizardry.tower_dimensions.tooltip=Lista de dimensiones donde se generaran torres de magos. +config.wizardry.spell_book_drop_chance.tooltip=La probabilidad de que un mob arroje un libro de hechizos cuando muera. Mientras mas alto sea el numero, mas chance tendras. Coloca un 0 para desactivar el dropeo de libros. Coloque 200 para asegurar el dropeo 100%. +config.wizardry.generate_loot.tooltip=Si se debe generar botin del mod en cofres de mazmorras. +config.wizardry.firebomb_is_craftable.tooltip=Si las bombas de fuego son crafteables o no. +config.wizardry.poison_bomb_is_craftable.tooltip=Si las bombas de veneno son crafteables o no. +config.wizardry.smoke_bomb_is_craftable.tooltip=Si las bombas de humo son crafteables o no. +config.wizardry.use_alternate_scroll_recipe.tooltip=Si se requiere cristal magico para craftear pergaminos vacioes. Utilice esta opcion si otro mod tiene conflicto. +config.wizardry.teleport_through_unbreakable_blocks.tooltip=Si los jugadores son capaces de pasar a traves de bloques indestructibles (bedrock) usando Paso de Fase. +config.wizardry.show_summoned_creature_names.tooltip=Si mostrar los nombres de las criaturas invocadas y sus duenos sobre su cabeza. +config.wizardry.friendly_fire.tooltip=Permitir que los aliados se puedan herir con magia. +config.wizardry.telekinetic_disarmament.tooltip=Permitir que los jugadores puedan desarmar otros jugadores con el hechizo de Telequinesis. +config.wizardry.discovery_mode.tooltip=Para aquellos que le guste el misterio! Si esta en verdadero, los hechizos que no se hayan conjurado seran ilegibles. No tiene efecto en creativo. +config.wizardry.enable_shift_scrolling.tooltip=Si eres capaz de cambiar de hechizos al agacharte y usar la rueda del raton. Nota: Esta funcionalidad es del lado cliente, otros jugadores no dependen de esta, deben configurar su propia opcion. +config.wizardry.minion_revenge_targeting.tooltip=Si las criaturas invocadas atacan a su invocador si este los ataca. +config.wizardry.player_damage_scaling.tooltip=Factor de incremento de dano global para el dano hecho por jugadores, relativo a 1. +config.wizardry.npc_damage_scaling.tooltip=Factor de incremento de dano global para el dano hecho por NPCs, relativo a 1. +config.wizardry.cast_command_multiplier_limit.tooltip=Limite maximo usado con el comando /cast. Esto esta aqui para evitar que accidentalmente destruyan un mundo o servidor. +config.wizardry.summoned_creature_targets_whitelist.tooltip=Lista de nombres de entidades que los magos y criaturas invocadas pueden atacar. +config.wizardry.summoned_creature_targets_blacklist.tooltip=Lista de nombres de entidades que los magos y criaturas invocadas no pueden atacar. Si eliminas creeper hazlo bajo tu responsabilidad!! +config.wizardry.spell_hud_position.tooltip=La posicion de la interfaz de hechizos. +config.wizardry.cast_command_name.tooltip=El nombre del comando /cast. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'magia', entonces el comando seria /magia. +config.wizardry.discoverspell_command_name.tooltip=El nombre del comando /discoverspell. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'descubrir', entonces el comando seria /descubrir. +config.wizardry.ally_command_name.tooltip=El nombre del comando /ally. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'aliado', entonces el comando seria /aliado. +config.wizardry.allies_command_name.tooltip=El nombre del comando /allies command. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'aliados', entonces el comando seria /aliados. + +config.wizardry.mobs_immune_to_fire.tooltip=Lista de nombres de entidades que son inmunes al fuego, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al fuego. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). +config.wizardry.mobs_immune_to_ice.tooltip=Lista de nombres de entidades que son inmunes al frio, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al frio. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). +config.wizardry.mobs_immune_to_lightning.tooltip=Lista de nombres de entidades que son inmunes al rayo, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al rayo. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). +config.wizardry.mobs_immune_to_wither.tooltip=Lista de nombres de entidades que son inmunes al wither, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al wither. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). +config.wizardry.mobs_immune_to_poison.tooltip=Lista de nombres de entidades que son inmunes al veneno, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al veneno. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). + +wizard.debug=%1$s, %2$s, %3$s \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/lang/ru_RU.lang b/src/main/resources/assets/wizardry/lang/ru_RU.lang new file mode 100644 index 00000000..3bd59b0c --- /dev/null +++ b/src/main/resources/assets/wizardry/lang/ru_RU.lang @@ -0,0 +1,519 @@ +tile.wizardry:arcane_workbench.name=ЧародейÑкий Стол +tile.wizardry:crystal_ore.name=КриÑÑ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð ÑƒÐ´Ð° +tile.wizardry:petrified_stone.name=Окаменелый Камень +tile.wizardry:ice_statue.name=ЛедÑÐ½Ð°Ñ Ð¡Ñ‚Ð°Ñ‚ÑƒÑ +tile.wizardry:crystal_flower.name=КриÑтальный Цветок +tile.wizardry:snare.name=Ловушка +tile.wizardry:transportation_stone.name=Камень ТранÑпортировки +tile.wizardry:spectral_block.name=Спектральный Блок +tile.wizardry:crystal_block.name=КриÑтальный Блок + +item.wizardry:magic_crystal.name=МагичеÑкий КриÑталл +item.wizardry:magic_wand.name=Волшебный Жезл +item.wizardry:apprentice_wand.name=Жезл Ученика +item.wizardry:advanced_wand.name=Продвинутый Жезл +item.wizardry:master_wand.name=Жезл МаÑтера +item.wizardry:spell_book.name=Книга Заклинаний + +item.wizardry:arcane_tome.name=Фолиант Ðрканы +item.wizardry:arcane_tome.desc1=Улучшает любые %1$s +item.wizardry:arcane_tome.desc2=жезл %1$s уровень + +item.wizardry:wizard_handbook.name=Книга Волшебника +item.wizardry:wizard_handbook.desc=от %1$s + +item.wizardry:wand.buff=+%1$s %2$s ÑффективноÑть +item.wizardry:wand.spell=Текущее Заклинание: %1$s +item.wizardry:wand.mana=Мана: %1$s/%2$s + +item.wizardry:basic_fire_wand.name=Жезл ÐžÐ³Ð½Ñ +item.wizardry:basic_ice_wand.name=Жезл Мороза +item.wizardry:basic_lightning_wand.name=Жезл ИÑкр +item.wizardry:basic_necromancy_wand.name=Жезл Теней +item.wizardry:basic_earth_wand.name=Жезл ЛеÑа +item.wizardry:basic_sorcery_wand.name=Жезл Тайн +item.wizardry:basic_healing_wand.name=Жезл Ð›ÐµÑ‡ÐµÐ½Ð¸Ñ + +item.wizardry:apprentice_fire_wand.name=Жезл Ученика Пироманта +item.wizardry:apprentice_ice_wand.name=Жезл Ученика ЛедÑного Мага +item.wizardry:apprentice_lightning_wand.name=Жезл Ученика Штормового Мага +item.wizardry:apprentice_necromancy_wand.name=Жезл Ученика Ðекроманта +item.wizardry:apprentice_earth_wand.name=Жезл Ученика ЗемлÑного Мага +item.wizardry:apprentice_sorcery_wand.name=Жезл Ученика Колдуна +item.wizardry:apprentice_healing_wand.name=Жезл Ученика Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ + +item.wizardry:advanced_fire_wand.name=Жезл Пироманта +item.wizardry:advanced_ice_wand.name=Жезл ЛедÑного Мага +item.wizardry:advanced_lightning_wand.name=Жезл Штормового Мага +item.wizardry:advanced_necromancy_wand.name=Жезл Ðекроманта +item.wizardry:advanced_earth_wand.name=Жезл ЗемлÑного Мага +item.wizardry:advanced_sorcery_wand.name=Жезл Колдуна +item.wizardry:advanced_healing_wand.name=Жезл Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ + +item.wizardry:master_fire_wand.name=Жезл МаÑтера Пироманта +item.wizardry:master_ice_wand.name=Жезл МаÑтера ЛедÑного Мага +item.wizardry:master_lightning_wand.name=Жезл МаÑтера Штормового Мага +item.wizardry:master_necromancy_wand.name=Жезл МаÑтера Ðекроманта +item.wizardry:master_earth_wand.name=Жезл МаÑтера ЗемлÑного Мага +item.wizardry:master_sorcery_wand.name=Жезл МаÑтера Колдуна +item.wizardry:master_healing_wand.name=Жезл МаÑтера Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ + +item.wizardry:spectral_sword.name=Спектральный Меч +item.wizardry:spectral_pickaxe.name=Ð¡Ð¿ÐµÐºÑ‚Ñ€Ð°Ð»ÑŒÐ½Ð°Ñ ÐšÐ¸Ñ€ÐºÐ° +item.wizardry:spectral_bow.name=Спектральный Лук + +item.wizardry:mana_flask.name=Бутылка Маны +item.wizardry:storage_upgrade.name=Улучшение Хранилища Жезла +item.wizardry:siphon_upgrade.name=Улучшение Сифона Жезла +item.wizardry:condenser_upgrade.name=Улучшение КонденÑатора Жезла +item.wizardry:range_upgrade.name=Улучшение РадиуÑа Жезла +item.wizardry:duration_upgrade.name=Улучшение ДлительноÑти Жезла +item.wizardry:cooldown_upgrade.name=Улучшение Кулдауна Жезла +item.wizardry:blast_upgrade.name=Улучшение ÐžÐ±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð–ÐµÐ·Ð»Ð° + +item.wizardry:flaming_axe.name=Огненный Топор +item.wizardry:frost_axe.name=ЛедÑной Топор + +item.wizardry:firebomb.name=ÐžÐ³Ð½ÐµÐ½Ð½Ð°Ñ Ð‘Ð¾Ð¼Ð±Ð° +item.wizardry:poison_bomb.name=Ð¯Ð´Ð¾Ð²Ð¸Ñ‚Ð°Ñ Ð‘Ð¾Ð¼Ð±Ð° + +item.wizardry:blank_scroll.name=ПуÑтой Свиток +item.wizardry:scroll.name=Свиток %1$s + +item.wizardry:armour_upgrade.name=Печать МагичеÑкой Защиты +item.wizardry:armour_upgrade.desc1=%1$sУлучшает Любые ОдеÑÐ½Ð¸Ñ ÐœÐ°Ð³Ð° +item.wizardry:armour_upgrade.desc2=%1$sÑделать Ñто %2$sлегендарным + +item.wizardry:magic_silk.name=МагичеÑкий Шёлк + +item.wizardry:wizard_armour.legendary=Легендарный +item.wizardry:wizard_armour.buff=-%1$s %2$s ÑтоимоÑть +item.wizardry:wizard_armour.mana=Мана: %1$s/%2$s + +item.wizardry:wizard_hat.name=ШлÑпа Волшебника +item.wizardry:wizard_robe.name=Роба Волшебника +item.wizardry:wizard_leggings.name=Поножи Волшебника +item.wizardry:wizard_boots.name=Ботинки Волшебника + +item.wizardry:wizard_hat_fire.name=ШлÑпа Пироманта +item.wizardry:wizard_robe_fire.name=Роба Пироманта +item.wizardry:wizard_leggings_fire.name=Поножи Пироманта +item.wizardry:wizard_boots_fire.name=Ботинки Пироманта + +item.wizardry:wizard_hat_ice.name=ШлÑпа ЛедÑного Мага +item.wizardry:wizard_robe_ice.name=Роба ЛедÑного Мага +item.wizardry:wizard_leggings_ice.name=Поножи ЛедÑного Мага +item.wizardry:wizard_boots_ice.name=Ботинки ЛедÑного Мага + +item.wizardry:wizard_hat_lightning.name=ШлÑпа Штормового Мага +item.wizardry:wizard_robe_lightning.name=Роба Штормового Мага +item.wizardry:wizard_leggings_lightning.name=Поножи Штормового Мага +item.wizardry:wizard_boots_lightning.name=Ботинки Штормового Мага + +item.wizardry:wizard_hat_necromancy.name=ШлÑпа Ðекроманта +item.wizardry:wizard_robe_necromancy.name=Роба Ðекроманта +item.wizardry:wizard_leggings_necromancy.name=Поножи Ðекроманта +item.wizardry:wizard_boots_necromancy.name=Ботинки Ðекроманта + +item.wizardry:wizard_hat_earth.name=ШлÑпа ЗемлÑного Мага +item.wizardry:wizard_robe_earth.name=Роба ЗемлÑного Мага +item.wizardry:wizard_leggings_earth.name=Поножи ЗемлÑного Мага +item.wizardry:wizard_boots_earth.name=Ботинки ЗемлÑного Мага + +item.wizardry:wizard_hat_sorcery.name=ШлÑпа Колдуна +item.wizardry:wizard_robe_sorcery.name=Роба Колдуна +item.wizardry:wizard_leggings_sorcery.name=Поножи Колдуна +item.wizardry:wizard_boots_sorcery.name=Ботинки Колдуна + +item.wizardry:wizard_hat_healing.name=ШлÑпа Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ +item.wizardry:wizard_robe_healing.name=Роба Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ +item.wizardry:wizard_leggings_healing.name=Поножи Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ +item.wizardry:wizard_boots_healing.name=Ботинки Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ + +item.wizardry:spawn_wizard.name=Спаун Волшебника + +entity.wizardry.zombie_minion.name=Зомби +entity.wizardry.skeleton_minion.name=Скелет +entity.wizardry.spider_minion.name=Паук +entity.wizardry.blaze_minion.name=Блейз +entity.wizardry.ice_wraith.name=ЛедÑной Призрак +entity.wizardry.lightning_wraith.name=Штормовой Призрак +entity.wizardry.shadow_wraith.name=Теневой Призрак +entity.wizardry.spirit_wolf.name=Призрачный Волк +entity.wizardry.spirit_horse.name=ÐŸÑ€Ð¸Ð·Ñ€Ð°Ñ‡Ð½Ð°Ñ Ð›Ð¾ÑˆÐ°Ð´ÑŒ +entity.wizardry.ice_giant.name=ЛедÑной Гигант +entity.wizardry.phoenix.name=Ð¤ÐµÐ½Ð¸ÐºÑ +entity.wizardry.wizard.name=Волшебник +entity.wizardry.magic_slime.name=МагичеÑкий Слизень +entity.wizardry.silverfish_minion.name=Чешуйница + +entity.wizardry.magic_missile.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.arc.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.spark_bomb.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.ice_shard.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.firebomb.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.poison_bomb.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.force_orb.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.spark.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.darkness_orb.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.fire_sigil.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.frost_sigil.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.lightning_sigil.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.lightning_arrow.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.firebolt.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.ice_charge.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.force_arrow.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.dart.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.lightning_disc.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.thunderbolt.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.decay.name=ÐœÐ°Ð³Ð¸Ñ + +entity.wizardry.black_hole.name=Ð§Ñ‘Ñ€Ð½Ð°Ñ Ð”Ñ‹Ñ€Ð° +entity.wizardry.shield.name=Щит +entity.wizardry.meteor.name=Метеор +entity.wizardry.blizzard.name=Метель +entity.wizardry.bubble.name=Пузыри +entity.wizardry.tornado.name=Торнадо +entity.wizardry.lightning_hammer.name=Молот Шторма +entity.wizardry.arrow_rain.name=Дождь Стрел +entity.wizardry.healing_aura.name=Ðура Ð›ÐµÑ‡ÐµÐ½Ð¸Ñ +entity.wizardry.forcefield.name=Силовое Поле +entity.wizardry.ring_of_fire.name=Кольцо ÐžÐ³Ð½Ñ + +item_group.wizardry=ВолшебÑтво +item_group.wizardryspells=Волшебные Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ + +achievement.crystal=Любопытный КриÑталл... +achievement.crystal.desc=Добудьте МагичеÑкий КриÑталл +achievement.arcane_initiate=ПоÑвÑщение Ð’ Магию +achievement.arcane_initiate.desc=Создайте Волшебную Палочку Из Золотого Самородка И МагичеÑкого КриÑталла +achievement.apprentice=Ученик Волшебника +achievement.apprentice.desc=ИÑпользуйте Фолиант Ðрканы Ð”Ð»Ñ Ð£Ð»ÑƒÑ‡ÑˆÐµÐ½Ð¸Ñ Ð–ÐµÐ·Ð»Ð° +achievement.master=МаÑтер Магии +achievement.master.desc=Получите Жезл МаÑтера +achievement.all_spells=Маг Ð’Ñех ПрофеÑÑий +achievement.all_spells.desc=ИÑпользуйте Каждое Заклинание Ð’ Игре +achievement.wizard_trade=МагичеÑÐºÐ°Ñ Ð¡Ð´ÐµÐ»ÐºÐ° +achievement.wizard_trade.desc=Купите Предмет У Волшебника +achievement.buy_master_spell=Знание-Сила +achievement.buy_master_spell.wizardry:desc=Купите Заклинание МаÑтера У Волшебника +achievement.freeze_blaze=Ðе Так Жарко +achievement.freeze_blaze.desc=Заморозьте Блейза +achievement.charge_creeper=Это Ð¡ÐµÐ¹Ñ‡Ð°Ñ Ð’Ð·Ð¾Ñ€Ð²Ñ‘Ñ‚ÑÑ +achievement.charge_creeper.desc="Случайно" ЗарÑдите Крипира +achievement.frankenstein=Франкенштейн +achievement.frankenstein.desc=Привратите Свинью Ð’ Свинозомби С Помощью Молнии +achievement.special_upgrade=ЧародейÑкое РемеÑло +achievement.special_upgrade.desc=Применить Специальное Улучшение Ð”Ð»Ñ Ð–ÐµÐ·Ð»Ð° +achievement.craft_flask=Это ÐœÐ°Ð³Ð¸Ñ Ð’ Бутылке! +achievement.craft_flask.desc=Создайте Ману Ð’ Бутылке +achievement.elemental=Стихии +achievement.elemental.desc=Получите Стихийный Жезл +achievement.armour_set=Теперь Ты ÐаÑтоÑщий Волшебник +achievement.armour_set.desc=Создайте И Экипируйте Полный Ðабор ОдеÑÐ½Ð¸Ñ Ð’Ð¾Ð»ÑˆÐµÐ±Ð½Ð¸ÐºÐ° +achievement.legendary=Легендарный +achievement.legendary.desc=Получите ЧаÑть Легендарного ОдеÑÐ½Ð¸Ñ Ð’Ð¾Ð»ÑˆÐµÐ±Ð½Ð¸ÐºÐ° +achievement.self_destruct=Обратно +achievement.self_destruct.desc=УбейÑÑ Ð¡Ð¾Ð±Ñтвенной Магией +achievement.pig_tornado=Ðе СейчаÑ... +achievement.pig_tornado.desc=Заедьте Ðа Свинье Ð’ Торнадо + +tile.wizardry:transportation_stone.confirm=Теперь вы будете возвращены Ñюда при иÑпользовании Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ %1$s +tile.wizardry:transportation_stone.invalid=Сначала вы должны Ñделать круг из 8 камней транÑпортировки! + +container.wizardry:arcane_workbench=ЧародейÑкий Стол +container.wizardry:arcane_workbench.apply=Применить +container.wizardry:arcane_workbench.mana=Мана: +container.wizardry:arcane_workbench.upgrades=Применённые УлучшениÑ: + +tier.basic=Ðачинающий +tier.apprentice=Ученик +tier.advanced=Продвинутый +tier.master=МаÑтер + +element.simple=Ðет +element.fire=Огонь +element.ice=Лёд +element.lightning=ÐœÐ¾Ð»Ð½Ð¸Ñ +element.necromancy=ÐÐµÐºÑ€Ð¾Ð¼Ð°Ð½Ñ‚Ð¸Ñ +element.earth=Ð—ÐµÐ¼Ð»Ñ +element.sorcery=КолдовÑтво +element.healing=ИÑцеление + +element.simple.wizard=Волшебник +element.fire.wizard=Пиромант +element.ice.wizard=ЛедÑной Мага +element.lightning.wizard=Штормовой Маг +element.necromancy.wizard=Ðекромант +element.earth.wizard=ЗемлÑной Маг +element.sorcery.wizard=Колдун +element.healing.wizard=Целитель + +spelltype.attack=Ðтака +spelltype.defence=Защита +spelltype.utility=Утилита +spelltype.minion=Миньон + +spell.disabled=%1$s было отключенно в конфиге + +spell.wizardry:agility=ЛовкоÑть +spell.wizardry:arc=Дуга +spell.wizardry:arrow_rain=Дождь Стрел +spell.wizardry:black_hole=Ð§Ñ‘Ñ€Ð½Ð°Ñ Ð”Ñ‹Ñ€Ð° +spell.wizardry:blink=Блинк +spell.wizardry:blizzard=Метель +spell.wizardry:bubble=Пузыри +spell.wizardry:chain_lightning=Ð¦ÐµÐ¿Ð½Ð°Ñ ÐœÐ¾Ð»Ð½Ð¸Ñ +spell.wizardry:conjure_bow=Призвать Лук +spell.wizardry:conjure_pickaxe=Призвать Кирку +spell.wizardry:conjure_sword=Призвать Меч +spell.wizardry:cure_effects=Эффект Ð›ÐµÑ‡ÐµÐ½Ð¸Ñ +spell.wizardry:darkness_orb=Ð¢Ñ‘Ð¼Ð½Ð°Ñ Ð¡Ñ„ÐµÑ€Ð° +spell.wizardry:dart=Дротик +spell.wizardry:decay=Гниение +spell.wizardry:detonate=Взрыв +spell.wizardry:diamondflesh=ÐÐ»Ð¼Ð°Ð·Ð½Ð°Ñ ÐšÐ¾Ð¶Ð° +spell.wizardry:entrapment=Захват +spell.wizardry:fireball=Огненный Шар +spell.wizardry:firebolt=Огненный Болт +spell.wizardry:firebomb=ÐžÐ³ÐµÐ½Ð½Ð°Ñ Ð‘Ð¾Ð¼Ð±Ð° +spell.wizardry:fire_resistance=ОгеÑтойкоÑть +spell.wizardry:fire_sigil=ÐžÐ³Ð½ÐµÐ½Ð½Ð°Ñ ÐŸÐµÑ‡Ð°Ñ‚ÑŒ +spell.wizardry:fireskin=ÐžÐ³Ð½ÐµÐ½Ð½Ð°Ñ ÐšÐ¾Ð¶Ð° +spell.wizardry:firestorm=Огненный Шторм +spell.wizardry:flame_ray=Дождь ÐžÐ³Ð½Ñ +spell.wizardry:flaming_axe=Огненный Топор +spell.wizardry:flight=Полёт +spell.wizardry:font_of_vitality=ИÑточник Жизненной Силы +spell.wizardry:force_arrow=Ð¡Ð¸Ð»Ð¾Ð²Ð°Ñ Ð¡Ñ‚Ñ€ÐµÐ»Ð° +spell.wizardry:forcefield=Силовое Поле +spell.wizardry:force_orb=Ð¡Ð¸Ð»Ð¾Ð²Ð°Ñ Ð¡Ñ„ÐµÑ€Ð° +spell.wizardry:forests_curse=ПроклÑтие ЛеÑа +spell.wizardry:freeze=Замораживание +spell.wizardry:frost_axe=ЛедÑной Топор +spell.wizardry:frost_ray=ЛедÑной Луч +spell.wizardry:frost_sigil=ЛедÑÐ½Ð°Ñ ÐŸÐµÑ‡Ð°Ñ‚ÑŒ +spell.wizardry:glide=Скольжение +spell.wizardry:greater_heal=Великое ИÑцеление +spell.wizardry:growth_aura=Ðура РоÑта +spell.wizardry:heal=ИÑцеление +spell.wizardry:heal_ally=ИÑцеление Союзника +spell.wizardry:healing_aura=Ðура ИÑÑ†ÐµÐ»ÐµÐ½Ð¸Ñ +spell.wizardry:homing_spark=СамонаводÑщаÑÑÑ Ð˜Ñкра +spell.wizardry:ice_age=Ледниковый Период +spell.wizardry:ice_charge=ЛедÑной ЗарÑд +spell.wizardry:ice_shard=ЛедÑной ОÑколок +spell.wizardry:ice_shroud=ЛедÑÐ½Ð°Ñ ÐšÐ¾Ð¶Ð° +spell.wizardry:ice_statue=ЛедÑÐ½Ð°Ñ Ð¡Ñ‚Ð°Ñ‚ÑƒÑ +spell.wizardry:ignite=ВоÑпламенение +spell.wizardry:invisibility=ÐевидимоÑть +spell.wizardry:invoke_weather=Вызов Погоды +spell.wizardry:ironflesh=Ð–ÐµÐ»ÐµÐ·Ð½Ð°Ñ ÐšÐ¾Ð¶Ð° +spell.wizardry:levitation=Ð›ÐµÐ²Ð¸Ñ‚Ð°Ñ†Ð¸Ñ +spell.wizardry:life_drain=Похищение Жизни +spell.wizardry:light=Свет +spell.wizardry:lightning_arrow=Стрела Молнии +spell.wizardry:lightning_bolt=Удар Молнии +spell.wizardry:lightning_disc=ДиÑк Молнии +spell.wizardry:lightning_hammer=Молот Молнии +spell.wizardry:lightning_ray=Луч Молнии +spell.wizardry:lightning_sigil=Печать Молнии +spell.wizardry:magic_missile=МагичеÑÐºÐ°Ñ Ð Ð°ÐºÐµÑ‚Ð° +spell.wizardry:metamorphosis=Превращение +spell.wizardry:meteor=Метеор +spell.wizardry:mind_control=Контроль Разума +spell.wizardry:none=[ПуÑтой Слот] +spell.wizardry:petrify=Окаменение +spell.wizardry:phase_step=Фазовый Шаг +spell.wizardry:plague_of_darkness=Чума Темноты +spell.wizardry:poison=Отравление +spell.wizardry:poison_bomb=Ð¯Ð´Ð¾Ð²Ð¸Ñ‚Ð°Ñ Ð‘Ð¾Ð¼Ð±Ð° +spell.wizardry:replenish_hunger=ÐаÑыщение +spell.wizardry:ring_of_fire=Кольцо ÐžÐ³Ð½Ñ +spell.wizardry:shadow_ward=Ð¢ÐµÐ½ÐµÐ²Ð°Ñ Ð—Ð°Ñ‰Ð¸Ñ‚Ð° +spell.wizardry:shield=Щит +spell.wizardry:shockwave=Ð£Ð´Ð°Ñ€Ð½Ð°Ñ Ð’Ð¾Ð»Ð½Ð° +spell.wizardry:silverfish_swarm=Рой Чешуйниц +spell.wizardry:slime=Слизь +spell.wizardry:snare=Ловушка +spell.wizardry:snowball=Снежок +spell.wizardry:spark_bomb=Бомба ИÑкр +spell.wizardry:spectral_pathway=Спектральный Путь +spell.wizardry:spider_swarm=Рой Пауков +spell.wizardry:static_aura=СтатичеÑÐºÐ°Ñ Ðура +spell.wizardry:summon_blaze=Призыв Блейза +spell.wizardry:summon_ice_giant=Призыв ЛедÑного Гиганта +spell.wizardry:summon_ice_wraith=Призыв ЛедÑного Призрака +spell.wizardry:summon_iron_golem=Призыв Железного Голема +spell.wizardry:summon_lightning_wraith=Призыв Штормового Призрака +spell.wizardry:summon_phoenix=Призыв ФеникÑа +spell.wizardry:summon_shadow_wraith=Призыв Тёмного Призрака +spell.wizardry:summon_skeleton=Призыв Скелета +spell.wizardry:summon_skeleton_legion=Призыв Легиона Скелета +spell.wizardry:summon_snow_golem=Призыв Снеговика +spell.wizardry:summon_spirit_horse=Призыв Призрачной Лошади +spell.wizardry:summon_spirit_wolf=Призыв Призрачного Волка +spell.wizardry:summon_wither_skeleton=Призыв Скелета-ИÑÑÑƒÑˆÐ¸Ñ‚ÐµÐ»Ñ +spell.wizardry:summon_zombie=Призыв Зомби +spell.wizardry:telekinesis=Телекинез +spell.wizardry:thunderbolt=Штормовой Удар +spell.wizardry:thunderstorm=Гроза +spell.wizardry:tornado=Торнадо +spell.wizardry:transience=БыÑтрота +spell.wizardry:transportation=ТранÑпортировка +spell.wizardry:vanishing_box=ИÑÑ‡ÐµÐ·Ð°ÑŽÑ‰Ð°Ñ ÐšÐ¾Ñ€Ð¾Ð±ÐºÐ° +spell.wizardry:wall_of_frost=Стена Мороза +spell.wizardry:water_breathing=Дыхание Под Водой +spell.wizardry:whirlwind=Вихрь +spell.wizardry:wither=ИÑÑушение +spell.wizardry:wither_skull=Череп ИÑÑÑƒÑˆÐ¸Ñ‚ÐµÐ»Ñ + +spell.wizardry:agility.desc=Дает Заклинателю Более Ð’Ñ‹Ñокую СкороÑть ÐŸÐµÑ€ÐµÐ´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð˜ Большой Прижок на 30 Секунд. +spell.wizardry:arc.desc=Ð’Ñ‹Ñтреливает ИÑкрой Молнии Ð’ Цель +spell.wizardry:arrow_rain.desc="Лучники, огонь!" +spell.wizardry:black_hole.desc=Разрывай РеальноÑть Ðа ЧаÑти +spell.wizardry:blink.desc=Телепортирует Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð’ Любой Блок, Ðа Который Он Указывает, Ð’ Определенном РадиуÑе. +spell.wizardry:blizzard.desc=Создает Зону Закрученного ЛедÑного Ветра, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð—Ð°Ð¼ÐµÐ´Ð»Ñет И ПоÑтоÑнно Повреждает Ð’Ñе, Что Попало Внутрь. Заклинатель ÐевоÑприимчив К Урону, Ðо Ð’Ñе Еще ЗамедлÑетÑÑ. +spell.wizardry:bubble.desc=СтрелÑет Потоком Пузырьков, Который Помещает СущеÑтво Ð’ Большой Пузырь, При Это Поднимает Его. Цель Будет Падать Или ПроÑто Получит Урон. +spell.wizardry:chain_lightning.desc=Ð’Ñ‹Ñтреливает ИÑкру Молнии У Цели, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð—Ð°Ñ‚ÐµÐ¼ ЦеплÑетÑÑ Ðš Дополнительным ЦелÑм До Двух Раз. +spell.wizardry:conjure_bow.desc=Создает Спектральный Лук С Ðеограниченными Стрелами, Который ДлитÑÑ 30 Секунд. +spell.wizardry:conjure_pickaxe.desc=Создает Спектральную Кирку Равной Силы Железной Кирке, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð”Ð»Ð¸Ñ‚ÑÑ 30 СÑекунд. +spell.wizardry:conjure_sword.desc=Создает Спектральный Меч Одинаковой Силы С Железным Мечом, Который ДлитÑÑ 30 Секунд. +spell.wizardry:cure_effects.desc=УдалÑет Ð’Ñе Эффекты Зелий, Которые Ð’ ÐаÑтоÑщее Ð’Ñ€ÐµÐ¼Ñ Ð’Ð¾Ð·Ð´ÐµÐ¹Ñтвуют Ðа ЗаклинателÑ, Хорошие Или Плохие. +spell.wizardry:darkness_orb.desc=Ð’Ñ‹Ñтреливает Медленно ПеремещающимÑÑ Ð‘Ð¾Ð»Ñ‚Ð¾Ð¼ Тёмной Энергии Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Которое Вызывает Гниение Ð’Ñего, Что Попадает. +spell.wizardry:dart.desc=Ð’Ñ‹Ñтреливает Дротик Ð’ Том Ðаправлении, Ð’ Котором Ð’Ñ‹ Указываете, Которое ÐаноÑит Урон И ОÑлаблÑет Его Цель. +spell.wizardry:decay.desc=Создает ПÑтно Гнили Ðа Земле, Которое Заражает Любое СущеÑтво, Которое Его КаÑаетÑÑ, Ð’Ñ‹Ð·Ñ‹Ð²Ð°Ñ Ð”Ð»Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ñ‹Ð¹ Урон Со Временем И РаÑпроÑтранÑÑ Ð‘Ð¾Ð»ÑŒÑˆÐµ Гнили, Где Бы Он Ðи Ходил. +spell.wizardry:detonate.desc=Вызывает Взрыв, Ð’ Указанном МеÑте, ÐаноÑÑ Ð£Ñ€Ð¾Ð½ Ð’Ñем ÐаходÑщимÑÑ ÐŸÐ¾Ð±Ð»Ð¸Ð·Ð¾Ñти СущеÑтвам, Ð’ÐºÐ»ÑŽÑ‡Ð°Ñ Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ, ЕÑли Они Слишком Близко. +spell.wizardry:diamondflesh.desc="Твои Стрелы Мне Ðе ПодходÑÑ‚!" +spell.wizardry:entrapment.desc=Заманивает Цель Ð’ Сферу Темноты, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐŸÐ¾Ð´Ð½Ð¸Ð¼Ð°ÐµÑ‚ Её Вверх И ПоÑтоÑнно Её Повреждает. +spell.wizardry:fireball.desc=ЗапуÑкает Огненный Шар Ð’ Том Ðаправлении, Ð’ Которое Ð’Ñ‹ Указали. +spell.wizardry:firebolt.desc=СтрелÑет Ðа Ðебольшом РаÑÑтоÑнии От ВаÑ. +spell.wizardry:firebomb.desc=БраÑает Зажигательную Бомбу Ð’ Ðаправлении, Которое Ð’Ñ‹ Указываете, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð’Ð·Ñ€Ñ‹Ð²Ð°ÐµÑ‚ÑÑ ÐŸÑ€Ð¸ Ударе, ÐŸÐ¾Ð´Ð¶Ð¸Ð³Ð°Ñ Ð¦ÐµÐ»Ð¸. +spell.wizardry:fire_resistance.desc=Даёт Сопротивление Огню Ðа 30% +spell.wizardry:fire_sigil.desc=Помещает МагичеÑкую Ловушку ÐžÐ³Ð½Ñ Ðа Землю, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон И Поджигает СущеÑтво, Которое ÐаÑтупило Ðа Ловушку +spell.wizardry:fireskin.desc=Покрывает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ ÐžÐ³Ð½Ñ‘Ð¼ Ðа 30 Секунд. ЕÑли Заклинатель Будет Ðтакован, То Враг ЗагоритÑÑ. +spell.wizardry:firestorm.desc="Я Дракон." +spell.wizardry:flame_ray.desc=Создает Поток Пламени Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указали, Который Поджигает И ПоÑтоÑнно Повреждает Цели. +spell.wizardry:flaming_axe.desc=Создает Огненный Топор, Который Поджигает Врагов При Попадании. ДлитÑÑ 30 Секунд. +spell.wizardry:flight.desc=ПарÑщий, Как Орёл. +spell.wizardry:font_of_vitality.desc=Это ПотрÑÑающе. +spell.wizardry:force_arrow.desc=СтрелÑет Стрелой Силы Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете. +spell.wizardry:forcefield.desc=Создает Силовое Поле Вокруг ЗаклинателÑ, Который Отталкивает СущеÑтв И ОтклонÑет СнарÑды. +spell.wizardry:force_orb.desc=ЗапуÑкает Сферу Силы, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон И ОтбраÑывает Близких СущеÑтв При Ударе. +spell.wizardry:forests_curse.desc="Как Ты ПоÑмел Войти Ð’ Мой ЛеÑ?!" +spell.wizardry:freeze.desc=Замораживает Цель Ðа 10 Секунд. Также Заморозит Воду И СоздаÑÑ‚ Снег Ðа Земле. +spell.wizardry:frost_axe.desc=Создает ЛедÑной Топор, Который Замораживает Врагов При Попадании. ДлитÑÑ 30 Секунд. +spell.wizardry:frost_ray.desc=Создает Поток Мороза Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Который ЗамедлÑет И ПоÑтоÑнно Повреждает Цели. +spell.wizardry:frost_sigil.desc=Помещает МагичеÑкую ЛедÑную Ловушку Ðа Землю, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон И Замораживает СущеÑтво, Которое ÐаÑтупило Ðа Ðеё. +spell.wizardry:glide.desc=ПозволÑет Заклинателю Скользить Вниз, ÐаходÑÑÑŒ Ð’ Воздухе, Ð£Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°Ñ ÐšÐ½Ð¾Ð¿ÐºÑƒ ИÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÐŸÑ€ÐµÐ´Ð¼ÐµÑ‚Ð°. +spell.wizardry:greater_heal.desc=ВоÑÑтанавливает Заклинателю 4 Сердца. +spell.wizardry:growth_aura.desc=Выращивает Ð’Ñе Зерновые Культуры РÑдом С Заклинателем. +spell.wizardry:heal.desc=ИÑцелÑет Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðа 2 Сердца. +spell.wizardry:heal_ally.desc=ВоÑÑтанавливает Цели 2 С Половиной Сердца. +spell.wizardry:healing_aura.desc=Создает Зону ИÑцелÑющей Энергии, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð’Ð¾ÑÑтанавливает Здоровье Кого-либо Внутри Ðего, Кроме Ðежити, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐœÐµÐ´Ð»ÐµÐ½Ð½Ð¾ Получает Урон. +spell.wizardry:homing_spark.desc=Создает Плавающую ИÑкру, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð”Ð²Ð¸Ð¶ÐµÑ‚ÑÑ Ðš Врагам. +spell.wizardry:ice_age.desc="Ð’Ñ‹ Замерзнете ÐавÑегда!" +spell.wizardry:ice_charge.desc=ЗапуÑкает ЗарÑд Льда, Который ВзрываетÑÑ ÐŸÑ€Ð¸ Ударе, Ð—Ð°Ð¼Ð¾Ñ€Ð°Ð¶Ð¸Ð²Ð°Ñ Ð‘Ð»Ð¸Ð·Ð»ÐµÐ¶Ð°Ñ‰Ð¸Ñ… СущеÑтв И ВыпуÑÐºÐ°Ñ ÐžÑколки Во Ð’Ñех ÐаправлениÑÑ…. +spell.wizardry:ice_shard.desc=Ð’Ñ‹Ñтреливает ЛедÑной ОÑколок Ð’ Том Ðаправлении, Ð’ Котором Ð’Ñ‹ Указали, Который ÐаноÑит Урон И ЗамедлÑет Цель При Попадании. +spell.wizardry:ice_shroud.desc=Окутывает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð’Ð¾ Льду Ðа 30 Секунд, Ð—Ð°Ð¼Ð¾Ñ€Ð°Ð¶Ð¸Ð²Ð°Ñ Ð’Ñе, Что Попадает Или Попадает. +spell.wizardry:ice_statue.desc=Замораживает Цель Ðа 20 Секунд Или До Тех Пор, Пока Она Ðе ВырветÑÑ Ðаружу. Цель Ðе Может ДвигатьÑÑ Ð˜Ð»Ð¸ Делать Что-либо Ð’ Замороженном СоÑтоÑнии, Ðо Также ÐевоÑприимчива К Любому Урону. +spell.wizardry:ignite.desc=УÑтанавливает Огонь Ð’ Течение 10 Секунд. Также Работает Как Кремень И Сталь. +spell.wizardry:invisibility.desc=Делает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðевидимым Ð’ Течение 30 Секунд. +spell.wizardry:invoke_weather.desc=ИзменÑет Погоду Ð’ Мире. +spell.wizardry:ironflesh.desc=Увеличивает Сопротивление К Оглушению Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðа 30 Секунд. +spell.wizardry:levitation.desc=Поднимает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð’Ð²ÐµÑ€Ñ… При Ðажатой Кнопке ИÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÐŸÑ€ÐµÐ´Ð¼ÐµÑ‚Ð°. Также Будет Ðейтрализован Урон От ПадениÑ, ЕÑли Он ИÑпользуетÑÑ Ð”Ð¾ Удара Об Землю. +spell.wizardry:life_drain.desc=Создает Поток ИÑÑушающей Энергии Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Который ИÑтощает Жизнь Цели И ИÑпользует Её Ð”Ð»Ñ ÐŸÐ¾Ñтепенного ВоÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð—Ð´Ð¾Ñ€Ð¾Ð²ÑŒÑ. +spell.wizardry:light.desc=Создает МагичеÑкую Точку Света, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐžÑвещает Окружающую ОблаÑть. ДлитÑÑ 30 Секунд. +spell.wizardry:lightning_arrow.desc=СтрелÑет Стрелой Молнии Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете. +spell.wizardry:lightning_bolt.desc=ЗаÑтавлÑет Молнию Ударить Туда, Куда Ð’Ñ‹ Указываете. +spell.wizardry:lightning_disc.desc=ПоÑылает ДиÑк Молнии, ЛетÑщий Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указали И Ищет Цели. +spell.wizardry:lightning_hammer.desc="Я Поражу Ð¢ÐµÐ±Ñ Ð“Ð½ÐµÐ²Ð¾Ð¼ ÐебеÑ!" +spell.wizardry:lightning_ray.desc=Создает Поток Молнии Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Который ПоÑтоÑнно Повреждает Цели. +spell.wizardry:lightning_sigil.desc=Помещает МагичеÑкую МолниеноÑную Ловушку Ðа Землю, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон СущеÑтву, Которое ÐаÑтупило Ðа Ловушку, И Приковывает Молнии К Другим Близлежащим СущеÑтвам. +spell.wizardry:magic_missile.desc=Ð’Ñ‹Ñтреливает МагичеÑкий ЗарÑд Ð’ Том Ðаправлении, Ð’ Котором Ð’Ñ‹ Указали. +spell.wizardry:metamorphosis.desc=ИзменÑет Цель Ð’ Другую Форму. Работает Только Ðа Ðекоторых СущеÑтвах. +spell.wizardry:meteor.desc=Ðекоторые Волшебники ПроÑто ХотÑÑ‚, Чтобы Мир Горел ... +spell.wizardry:mind_control.desc=Контролирует Ум Цели, ЗаÑтавлÑÑ Ð•Ñ‘ Ðтаковать Ближайшую Живую Цель, Отличную От ЗаклинателÑ. Когда Это СущеÑтво Будет Убито, Цель ВернетÑÑ Ðš Ðормальной Жизни. +spell.wizardry:none.desc=Чтобы Получить Книгу Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ /give command, иÑпользовать метаданные: /give [player] wizardry:spell_book 1 [spell id] (if you found this book in a chest, some other mod has messed things up). +spell.wizardry:petrify.desc=Повергает Цель Ð’ Камень До Следующего Заката Или Пока Ðе Будет Разбит. Цель Ðе Может ДвигатьÑÑ Ð˜Ð»Ð¸ Делать Что-либо, Пока Она Окаменела, Ðо Также ÐевоÑприимчива К Любому Урону. +spell.wizardry:phase_step.desc=Телепортирует Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð§ÐµÑ€ÐµÐ· Стену Толщиной 1 Блок Перед Ðим. Улучшение ДальноÑти Увеличит Толщину, Через Которую Ð’Ñ‹ Сможете ТелепортироватьÑÑ. +spell.wizardry:plague_of_darkness.desc=Тьма Поглотит Их Ð’Ñех... +spell.wizardry:poison.desc=Ð’Ñ‹Ñтреливает Яд Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете. +spell.wizardry:poison_bomb.desc=БроÑает Ядовитую Бомбу Ð’ Ðаправлении, Которое Ð’Ñ‹ Указываете, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð’Ð·Ñ€Ñ‹Ð²Ð°ÐµÑ‚ÑÑ ÐŸÑ€Ð¸ Ударе, ОтравлÑÑ Ð‘Ð»Ð¸Ð·ÐºÐ¸Ñ… СущеÑтв. +spell.wizardry:replenish_hunger.desc=ПополнÑет Ð—Ð°Ð¿Ð°Ñ ÐŸÐ¸Ñ‰Ð¸ Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðа 6 Пунктов Голода. +spell.wizardry:ring_of_fire.desc=Создает Огненное Кольцо Вокруг ЗаклинателÑ, ÐаноÑÑ Ð£Ñ€Ð¾Ð½ Ð’Ñем Ближайшим Противникам И ÐŸÐ¾Ð´Ð¶Ð¸Ð³Ð°Ñ Ð˜Ñ…. +spell.wizardry:shadow_ward.desc=Создает Стену Тьмы Перед Заклинателем, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Ðападающему Половину Ð’Ñего ВходÑщего Урона. +spell.wizardry:shield.desc=Создает Защитный Барьер Силы, Который Блокирует СнарÑды И Магию. Также Дает Заклинателю Слабый Эффект СопротивлениÑ. +spell.wizardry:shockwave.desc=Бум. +spell.wizardry:silverfish_swarm.desc="ÐÐÐÐÐ! ИХ ОЧЕÐЬ МÐОГО!" +spell.wizardry:slime.desc=Охватывает Цель Слизью, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð—Ð°Ð¼ÐµÐ´Ð»Ñет И ПоÑтоÑнно Её Повреждает. Слизь РазрываетÑÑ Ð§ÐµÑ€ÐµÐ· 10 Секунд. +spell.wizardry:snare.desc=УÑтанавливает Ловушку Ðа Земле, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон И Кратко ЗамедлÑет СущеÑтво, Которое ÐаÑтупило Ðа Ловушку. +spell.wizardry:snowball.desc=Launches a snowball in the direction you are pointing. +spell.wizardry:spark_bomb.desc=ЗапуÑкает Бомбу ИÑкр Ð’ Том Ðаправлении, Ð’ Котором Ð’Ñ‹ Указываете. ИÑкры ПоÑвлÑÑŽÑ‚ÑÑ Ð£ Ближайших Противников При Ударе. +spell.wizardry:spectral_pathway.desc=Создает Перед Вами Ðеразрушимый МагичеÑкий МоÑÑ‚, Который ПроÑтираетÑÑ Ðа 15 Блоков. Через 60 Секунд МоÑÑ‚ ИÑчезает. +spell.wizardry:spider_swarm.desc=Призывает Рой Ядовитых Пауков, Чтобы СражатьÑÑ Ð—Ð° ВаÑ. Пауки ИÑчезнут Через 30 Секунд Или ЕÑли Они Будут Убиты. +spell.wizardry:static_aura.desc=Окружает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ ÐœÐ¾Ð»Ð½Ð¸ÐµÐ¹ Ð’ Течение 30 Секунд, СтрелÑет ИÑкрами Молнии Во Ð’Ñе, Что Ударит ВаÑ. +spell.wizardry:summon_blaze.desc=Призывает Блейза, Чтобы СражатьÑÑ Ð—Ð° ВаÑ. Блейз ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. +spell.wizardry:summon_ice_giant.desc="Разбей Их!" +spell.wizardry:summon_ice_wraith.desc=Призывает ЛедÑного Призрака, Который СражаетÑÑ Ð—Ð° ВаÑ. ЛедÑной Призрак ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. +spell.wizardry:summon_iron_golem.desc=ÐвтоматичеÑкий Ðвтономный Ðвтомат. +spell.wizardry:summon_lightning_wraith.desc=Призывает МолниеноÑного Призрака СражатьÑÑ Ð—Ð° ВаÑ. МолниеноÑный Призрак ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. +spell.wizardry:summon_phoenix.desc=Из Пепла... +spell.wizardry:summon_shadow_wraith.desc=Призывает Тёмного Призрака, Который Будет СражатьÑÑ Ð—Ð° ВаÑ. +spell.wizardry:summon_skeleton.desc=Призывает Скелета, Который СражаетÑÑ Ð—Ð° ваÑ. Скелет ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. +spell.wizardry:summon_skeleton_legion.desc="ВоÑÑтань, ÐÑ€Ð¼Ð¸Ñ Ðежети!" +spell.wizardry:summon_snow_golem.desc=Создает Снежного Голема, Который СражаетÑÑ Ð—Ð° ВаÑ. ДлитÑÑ Ð”Ð¾ Тех Пор, Пока Ðе Умрет Снежный Голем. +spell.wizardry:summon_spirit_horse.desc=Призывает Ð”Ð»Ñ Ð’Ð°Ñ Ð’ÐµÑ€Ñ…Ð¾Ð²ÑƒÑŽ Лошадь. ÐŸÑ€Ð¸Ð·Ñ€Ð°Ñ‡Ð½Ð°Ñ Ð›Ð¾ÑˆÐ°Ð´ÑŒ ИÑчезнет Через Ðекоторое Ð’Ñ€ÐµÐ¼Ñ ÐŸÐ¾Ñле Того, Как Ð’Ñ‹ С Ðеё Слезите, Или Ð’Ñ‹ Можете Убрать Её С Помощью Жезла - Щелкните Правой Кнопкой Мыши Ðа Ðей. +spell.wizardry:summon_spirit_wolf.desc=Призывает Спутника Духа Волка, Который СражаетÑÑ Ð—Ð° ВаÑ. Призрачный Волк ИÑчезнет, Только ЕÑли Его Убьют, Или Ð’Ñ‹ Можете Его Убрать, Щелкнув Правой Кнопкой Мыши Ðа Ðём С Помощью Жезла. +spell.wizardry:summon_wither_skeleton.desc=Призывает Скелета-ИÑÑушителÑ, Который СражаетÑÑ Ð—Ð° ВаÑ. Скелет-ИÑÑушитель ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. +spell.wizardry:summon_zombie.desc=Призывает Зомби, Который СражаетÑÑ Ð—Ð° ВаÑ. Зомби ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. +spell.wizardry:telekinesis.desc=Перемещает Объект Или Другой Маленький Объект К Себе Или Щелкает Правой Кнопкой Мыши Ðа Блоке,Ðа Который Ð’Ñ‹ Смотрите. +spell.wizardry:thunderbolt.desc=СтрелÑет Громом, Который ОтбраÑывает Цели. +spell.wizardry:thunderstorm.desc="МУХÐÐ¥ÐÐ¥ÐÐ¥ÐÐ¥!" +spell.wizardry:tornado.desc=РазвÑзывает Торнадо Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Который БроÑает Что-либо Ðа Своем Пути Ð’ Ðебо. +spell.wizardry:transience.desc=Делает Переход Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðа 20 Секунд. Заклинатель ÐевоÑприимчив Ко Ð’Ñему Урону Ð’ Переходный Период, Ðо Ðе Может Сломать Или ПомеÑтить Блоки Или ÐанеÑти Какой-либо Урон. +spell.wizardry:transportation.desc=ТранÑпортирует Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð’ Запомненный Каменный Круг. Чтобы ИÑпользовать Это Заклинание, Сделайте Круг Камней ТранÑпортировки, Затем Щелкните Правой Кнопкой Мыши С Помощью Жезла. +spell.wizardry:vanishing_box.desc=Grants the caster access to their ender chest storage. +spell.wizardry:wall_of_frost.desc=Зима У Ð’Ð°Ñ ÐŸÐ¾Ð´ Рукой. +spell.wizardry:water_breathing.desc=ПозволÑет Заклинателю Дышать Под Водой Ð’ Течение 60 Секунд. +spell.wizardry:whirlwind.desc=ЗаÑтавлÑет Цель Взлететь Вверх Далеко От Ð’Ð°Ñ Ðа СкороÑти. +spell.wizardry:wither.desc=СтрелÑет Лучом Тьмы, Который УвÑдает Ð’ÑÑ‘, К Чему Он ПрикаÑаетÑÑ. +spell.wizardry:wither_skull.desc=ЗапуÑкает Череп ИÑÑÑƒÑˆÐ¸Ñ‚ÐµÐ»Ñ Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете. + +spell.wizardry:invoke_weather.sun=Дождь Ðачинает ОÑтанавливатьÑÑ ... +spell.wizardry:invoke_weather.rain=ÐебеÑа ОткрываютÑÑ ... +spell.wizardry:transportation.missing=Ваш Запомненный Каменный Круг ОтÑутÑтвует Или Затруднен ... +spell.wizardry:transportation.undefined=Ð’Ñ‹ Должны Сначала Запомнить МеÑтоположение Каменного Круга! +spell.wizardry:transportation.wrongdimension=Ваш Запомненный Каменный Круг ÐаходитÑÑ Ð’ Другом Измерении ... + +potion.wizardry:frost=Отморожение +potion.wizardry:fireskin=ÐžÐ³Ð½ÐµÐ½Ð½Ð°Ñ ÐšÐ¾Ð¶Ð° +potion.wizardry:ice_shroud=ЛедÑÐ½Ð°Ñ ÐšÐ¾Ð¶Ð° +potion.wizardry:static_aura=СтатичеÑÐºÐ°Ñ Ðура +potion.wizardry:transience=БыÑтрота +potion.wizardry:decay=Гниение + +key.categories.wizardry=Wizardry + +death.attack.wizardry_magic=%1$s был убит %2$s иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð¼Ð°Ð³Ð¸ÑŽ +death.attack.indirect_wizardry_magic=%1$s был убит %2$s иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð¼Ð°Ð³Ð¸ÑŽ + +config.wizardry.title.general=Mod Options +config.wizardry.title.spells=Spell Configuration +config.wizardry.subtitle.spells=Set a spell to false to disable it. + +config.wizardry.tower_rarity=Tower Rarity +config.wizardry.ore_dimensions=Ore Dimensions +config.wizardry.flower_dimensions=Flower Dimensions +config.wizardry.tower_dimensions=Tower Dimensions +config.wizardry.spell_book_drop_chance=Spell Book Drop Chance +config.wizardry.generate_loot=Generate Loot +config.wizardry.firebomb_is_craftable=Firebomb Is Craftable +config.wizardry.poison_bomb_is_craftable=Poison Bomb Is Craftable +config.wizardry.use_alternate_scroll_recipe=Use Alternate Scroll Recipe +config.wizardry.teleport_through_unbreakable_blocks=Teleport Through Unbreakable Blocks +config.wizardry.show_summoned_creature_names=Show Summoned Creature Names +config.wizardry.spell_hud_position=Spell HUD Position + +config.wizardry.category.spells=Configure Spells +config.wizardry.category.spells.tooltip=Select which spells are enabled. \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/chests/dungeon_additions.json b/src/main/resources/assets/wizardry/loot_tables/chests/dungeon_additions.json new file mode 100644 index 00000000..59c4bf30 --- /dev/null +++ b/src/main/resources/assets/wizardry/loot_tables/chests/dungeon_additions.json @@ -0,0 +1,92 @@ +{ + "pools": [ + { + "name": "wizardry", + "rolls": { + "min": 3, + "max": 7 + }, + "entries": [ + { + "type": "loot_table", + "name": "wizardry:subsets/novice_wands", + "weight": 3 + }, + { + "type": "loot_table", + "name": "wizardry:subsets/wizard_armour", + "weight": 6 + }, + { + "type": "loot_table", + "name": "wizardry:subsets/arcane_tomes", + "weight": 6 + }, + { + "type": "loot_table", + "name": "wizardry:subsets/wand_upgrades", + "weight": 2 + }, + { + "type": "item", + "name": "wizardry:magic_crystal", + "weight": 5, + "functions": [ + { + "function": "set_count", + "count": { + "min": 1, + "max": 4 + } + } + ] + }, + { + "type": "item", + "name": "wizardry:spell_book", + "weight": 20, + "functions": [ + { + "function": "wizardry:random_spell" + } + ] + }, + { + "type": "item", + "name": "wizardry:scroll", + "weight": 10, + "functions": [ + { + "function": "wizardry:random_spell" + } + ] + }, + { + "type": "item", + "name": "wizardry:armour_upgrade", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:identification_scroll", + "weight": 3 + }, + { + "type": "item", + "name": "wizardry:firebomb", + "weight": 4 + }, + { + "type": "item", + "name": "wizardry:poison_bomb", + "weight": 4 + }, + { + "type": "item", + "name": "wizardry:smoke_bomb", + "weight": 4 + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/chests/wizard_tower.json b/src/main/resources/assets/wizardry/loot_tables/chests/wizard_tower.json new file mode 100644 index 00000000..f7bba6ea --- /dev/null +++ b/src/main/resources/assets/wizardry/loot_tables/chests/wizard_tower.json @@ -0,0 +1,77 @@ +{ + "pools": [ + { + "name": "wizardry", + "rolls": { + "min": 3, + "max": 7 + }, + "entries": [ + { + "type": "loot_table", + "name": "wizardry:subsets/novice_wands", + "weight": 3 + }, + { + "type": "loot_table", + "name": "wizardry:subsets/wizard_armour", + "weight": 6 + }, + { + "type": "loot_table", + "name": "wizardry:subsets/arcane_tomes", + "weight": 6 + }, + { + "type": "loot_table", + "name": "wizardry:subsets/wand_upgrades", + "weight": 2 + }, + { + "type": "item", + "name": "wizardry:magic_crystal", + "weight": 5, + "functions": [ + { + "function": "set_count", + "count": { + "min": 1, + "max": 4 + } + } + ] + }, + { + "type": "item", + "name": "wizardry:spell_book", + "weight": 20, + "functions": [ + { + "function": "wizardry:random_spell" + } + ] + }, + { + "type": "item", + "name": "wizardry:scroll", + "weight": 10, + "functions": [ + { + "function": "wizardry:random_spell" + } + ] + }, + { + "type": "item", + "name": "wizardry:armour_upgrade", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:identification_scroll", + "weight": 3 + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/entities/evil_wizard.json b/src/main/resources/assets/wizardry/loot_tables/entities/evil_wizard.json new file mode 100644 index 00000000..8723be6f --- /dev/null +++ b/src/main/resources/assets/wizardry/loot_tables/entities/evil_wizard.json @@ -0,0 +1,55 @@ +{ + "pools": [ + { + "name": "common", + "rolls": 1, + "entries": [ + { + "type": "item", + "name": "wizardry:magic_crystal", + "weight": 1, + "functions": [ + { + "function": "set_count", + "count": { + "min": 3, + "max": 5 + }, + "function": "looting_enchant", + "count": { + "min": 0, + "max": 1 + } + } + ] + } + ] + }, + { + "name": "rare", + "conditions": [ + { + "condition": "killed_by_player" + }, + { + "condition": "random_chance_with_looting", + "chance": 0.05, + "looting_multiplier": 0.01 + } + ], + "rolls": 1, + "entries": [ + { + "type": "item", + "name": "wizardry:spell_book", + "weight": 1, + "functions": [ + { + "function": "wizardry:wizard_spell" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/entities/mob_additions.json b/src/main/resources/assets/wizardry/loot_tables/entities/mob_additions.json new file mode 100644 index 00000000..dc872ac7 --- /dev/null +++ b/src/main/resources/assets/wizardry/loot_tables/entities/mob_additions.json @@ -0,0 +1,29 @@ +{ + "pools": [ + { + "conditions": [ + { + "condition": "killed_by_player" + }, + { + "condition": "random_chance_with_looting", + "chance": 0.02, + "looting_multiplier": 0.01 + } + ], + "rolls": 1, + "entries": [ + { + "type": "item", + "name": "wizardry:spell_book", + "weight": 1, + "functions": [ + { + "function": "wizardry:random_spell" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/subsets/arcane_tomes.json b/src/main/resources/assets/wizardry/loot_tables/subsets/arcane_tomes.json new file mode 100644 index 00000000..14ca2ad4 --- /dev/null +++ b/src/main/resources/assets/wizardry/loot_tables/subsets/arcane_tomes.json @@ -0,0 +1,46 @@ +{ + "pools": [ + { + "name": "tomes", + "rolls": 1, + "entries": [ + { + "type": "item", + "entryName": "apprentice_tome", + "name": "wizardry:arcane_tome", + "weight": 3, + "functions": [ + { + "function": "set_data", + "data": 1 + } + ] + }, + { + "type": "item", + "entryName": "advanced_tome", + "name": "wizardry:arcane_tome", + "weight": 2, + "functions": [ + { + "function": "set_data", + "data": 2 + } + ] + }, + { + "type": "item", + "entryName": "master_tome", + "name": "wizardry:arcane_tome", + "weight": 1, + "functions": [ + { + "function": "set_data", + "data": 3 + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/subsets/novice_wands.json b/src/main/resources/assets/wizardry/loot_tables/subsets/novice_wands.json new file mode 100644 index 00000000..01f5c608 --- /dev/null +++ b/src/main/resources/assets/wizardry/loot_tables/subsets/novice_wands.json @@ -0,0 +1,50 @@ +{ + "pools": [ + { + "name": "wands", + "rolls": 1, + "entries": [ + { + "type": "item", + "name": "wizardry:magic_wand", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:basic_fire_wand", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:basic_ice_wand", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:basic_lightning_wand", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:basic_necromancy_wand", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:basic_earth_wand", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:basic_sorcery_wand", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:basic_healing_wand", + "weight": 1 + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/subsets/wand_upgrades.json b/src/main/resources/assets/wizardry/loot_tables/subsets/wand_upgrades.json new file mode 100644 index 00000000..4ce76637 --- /dev/null +++ b/src/main/resources/assets/wizardry/loot_tables/subsets/wand_upgrades.json @@ -0,0 +1,50 @@ +{ + "pools": [ + { + "name": "upgrades", + "rolls": 1, + "entries": [ + { + "type": "item", + "name": "wizardry:condenser_upgrade", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:siphon_upgrade", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:storage_upgrade", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:range_upgrade", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:duration_upgrade", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:cooldown_upgrade", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:blast_upgrade", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:attunement_upgrade", + "weight": 1 + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/subsets/wizard_armour.json b/src/main/resources/assets/wizardry/loot_tables/subsets/wizard_armour.json new file mode 100644 index 00000000..95f8ac07 --- /dev/null +++ b/src/main/resources/assets/wizardry/loot_tables/subsets/wizard_armour.json @@ -0,0 +1,170 @@ +{ + "pools": [ + { + "name": "armour", + "rolls": 1, + "entries": [ + { + "type": "item", + "name": "wizardry:wizard_hat", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_hat_fire", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_hat_ice", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_hat_lightning", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_hat_necromancy", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_hat_earth", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_hat_sorcery", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_hat_healing", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_robe", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_robe_fire", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_robe_ice", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_robe_lightning", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_robe_necromancy", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_robe_earth", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_robe_sorcery", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_robe_healing", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_leggings", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_leggings_fire", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_leggings_ice", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_leggings_lightning", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_leggings_necromancy", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_leggings_earth", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_leggings_sorcery", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_leggings_healing", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_boots", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_boots_fire", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_boots_ice", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_boots_lightning", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_boots_necromancy", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_boots_earth", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_boots_sorcery", + "weight": 1 + }, + { + "type": "item", + "name": "wizardry:wizard_boots_healing", + "weight": 1 + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/block/arcane_workbench.json b/src/main/resources/assets/wizardry/models/block/arcane_workbench.json new file mode 100644 index 00000000..8a545d1c --- /dev/null +++ b/src/main/resources/assets/wizardry/models/block/arcane_workbench.json @@ -0,0 +1,21 @@ +{ "parent": "block/block", + "textures": { + "particle": "wizardry:blocks/arcane_workbench_bottom", + "bottom": "wizardry:blocks/arcane_workbench_bottom", + "top": "wizardry:blocks/arcane_workbench_top", + "side": "wizardry:blocks/arcane_workbench_side" + }, + "elements": [ + { "from": [ 0, 0, 0 ], + "to": [ 16, 12, 16 ], + "faces": { + "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#bottom", "cullface": "down" }, + "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" }, + "north": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "north" }, + "south": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "south" }, + "west": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "west" }, + "east": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "east" } + } + } + ] +} diff --git a/src/main/resources/assets/wizardry/models/block/crystal_block.json b/src/main/resources/assets/wizardry/models/block/crystal_block.json new file mode 100644 index 00000000..898d595a --- /dev/null +++ b/src/main/resources/assets/wizardry/models/block/crystal_block.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "wizardry:blocks/crystal_block" + } +} diff --git a/src/main/resources/assets/wizardry/models/block/crystal_flower.json b/src/main/resources/assets/wizardry/models/block/crystal_flower.json new file mode 100644 index 00000000..90f2f653 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/block/crystal_flower.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cross", + "textures": { + "cross": "wizardry:blocks/crystal_flower" + } +} diff --git a/src/main/resources/assets/wizardry/models/block/crystal_ore.json b/src/main/resources/assets/wizardry/models/block/crystal_ore.json new file mode 100644 index 00000000..80b71f13 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/block/crystal_ore.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "wizardry:blocks/crystal_ore" + } +} diff --git a/src/main/resources/assets/wizardry/models/block/ice_statue.json b/src/main/resources/assets/wizardry/models/block/ice_statue.json new file mode 100644 index 00000000..b7f5c4ad --- /dev/null +++ b/src/main/resources/assets/wizardry/models/block/ice_statue.json @@ -0,0 +1,3 @@ +{ + "parent": "block/ice" +} diff --git a/src/main/resources/assets/wizardry/models/block/magic_light.json b/src/main/resources/assets/wizardry/models/block/magic_light.json new file mode 100644 index 00000000..1f4f2185 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/block/magic_light.json @@ -0,0 +1,16 @@ +{ + "parent": "block/block", + "elements": [ + { "from": [ 0, 0, 0 ], + "to": [ 0, 0, 0 ], + "faces": { + "down": { "texture": "#down", "cullface": "down" }, + "up": { "texture": "#up", "cullface": "up" }, + "north": { "texture": "#north", "cullface": "north" }, + "south": { "texture": "#south", "cullface": "south" }, + "west": { "texture": "#west", "cullface": "west" }, + "east": { "texture": "#east", "cullface": "east" } + } + } + ] +} diff --git a/src/main/resources/assets/wizardry/models/block/meteor.json b/src/main/resources/assets/wizardry/models/block/meteor.json new file mode 100644 index 00000000..52676102 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/block/meteor.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "wizardry:blocks/meteor" + } +} diff --git a/src/main/resources/assets/wizardry/models/block/petrified_stone.json b/src/main/resources/assets/wizardry/models/block/petrified_stone.json new file mode 100644 index 00000000..9ecdfbf2 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/block/petrified_stone.json @@ -0,0 +1,20 @@ +{ + "parent": "block/block", + "textures": { + "all": "blocks/stone", + "particle": "blocks/stone" + }, + "elements": [ + { "from": [ 0, 0, 0 ], + "to": [ 0, 0, 0 ], + "faces": { + "down": { "texture": "#down", "cullface": "down" }, + "up": { "texture": "#up", "cullface": "up" }, + "north": { "texture": "#north", "cullface": "north" }, + "south": { "texture": "#south", "cullface": "south" }, + "west": { "texture": "#west", "cullface": "west" }, + "east": { "texture": "#east", "cullface": "east" } + } + } + ] +} diff --git a/src/main/resources/assets/wizardry/models/block/snare.json b/src/main/resources/assets/wizardry/models/block/snare.json new file mode 100644 index 00000000..52b4fbd9 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/block/snare.json @@ -0,0 +1,16 @@ +{ + "ambientocclusion": false, + "textures": { + "particle": "wizardry:blocks/snare", + "texture": "wizardry:blocks/snare" + }, + "elements": [ + { "from": [ 0, 0.25, 0 ], + "to": [ 16, 0.25, 16 ], + "faces": { + "down": { "uv": [ 16, 16, 0, 0 ], "texture": "#texture"}, + "up": { "uv": [ 16, 0, 0, 16 ], "texture": "#texture"} + } + } + ] +} diff --git a/src/main/resources/assets/wizardry/models/block/spectral_block.json b/src/main/resources/assets/wizardry/models/block/spectral_block.json new file mode 100644 index 00000000..fd4a9d44 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/block/spectral_block.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "wizardry:blocks/spectral_block" + } +} diff --git a/src/main/resources/assets/wizardry/models/block/transportation_stone.json b/src/main/resources/assets/wizardry/models/block/transportation_stone.json new file mode 100644 index 00000000..9e235690 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/block/transportation_stone.json @@ -0,0 +1,20 @@ +{ + "parent": "block/block", + "textures": { + "all": "wizardry:blocks/transportation_stone", + "particle": "wizardry:blocks/transportation_stone" + }, + "elements": [ + { "from": [ 5, 0, 5 ], + "to": [ 11, 6, 11 ], + "faces": { + "down": { "texture": "#all", "cullface": "down" }, + "up": { "texture": "#all"}, + "north": { "texture": "#all"}, + "south": { "texture": "#all"}, + "west": { "texture": "#all"}, + "east": { "texture": "#all"} + } + } + ] +} diff --git a/src/main/resources/assets/wizardry/models/block/vanishing_cobweb.json b/src/main/resources/assets/wizardry/models/block/vanishing_cobweb.json new file mode 100644 index 00000000..f8865a41 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/block/vanishing_cobweb.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cross", + "textures": { + "cross": "blocks/web" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_earth_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_earth_wand.json new file mode 100644 index 00000000..3f23fbc6 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/advanced_earth_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_advanced_earth" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_fire_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_fire_wand.json new file mode 100644 index 00000000..beb8fc84 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/advanced_fire_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_advanced_fire" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_healing_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_healing_wand.json new file mode 100644 index 00000000..bba06e50 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/advanced_healing_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_advanced_healing" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_ice_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_ice_wand.json new file mode 100644 index 00000000..35dadf70 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/advanced_ice_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_advanced_ice" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_lightning_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_lightning_wand.json new file mode 100644 index 00000000..7c96d092 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/advanced_lightning_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_advanced_lightning" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_necromancy_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_necromancy_wand.json new file mode 100644 index 00000000..db36e099 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/advanced_necromancy_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_advanced_necromancy" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_sorcery_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_sorcery_wand.json new file mode 100644 index 00000000..127415e7 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/advanced_sorcery_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_advanced_sorcery" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_wand.json new file mode 100644 index 00000000..bee14a76 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/advanced_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_advanced" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_earth_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_earth_wand.json new file mode 100644 index 00000000..1ec46893 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/apprentice_earth_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_apprentice_earth" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_fire_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_fire_wand.json new file mode 100644 index 00000000..0aa6cf8e --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/apprentice_fire_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_apprentice_fire" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_healing_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_healing_wand.json new file mode 100644 index 00000000..118d72d0 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/apprentice_healing_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_apprentice_healing" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_ice_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_ice_wand.json new file mode 100644 index 00000000..25bbce5c --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/apprentice_ice_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_apprentice_ice" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_lightning_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_lightning_wand.json new file mode 100644 index 00000000..2f508113 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/apprentice_lightning_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_apprentice_lightning" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_necromancy_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_necromancy_wand.json new file mode 100644 index 00000000..eb662348 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/apprentice_necromancy_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_apprentice_necromancy" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_sorcery_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_sorcery_wand.json new file mode 100644 index 00000000..4ac8e0da --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/apprentice_sorcery_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_apprentice_sorcery" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_wand.json new file mode 100644 index 00000000..57a642e0 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/apprentice_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_apprentice" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/arcane_tome.json b/src/main/resources/assets/wizardry/models/item/arcane_tome.json new file mode 100644 index 00000000..9737284c --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/arcane_tome.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/arcane_tome" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/arcane_workbench.json b/src/main/resources/assets/wizardry/models/item/arcane_workbench.json new file mode 100644 index 00000000..35acd55b --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/arcane_workbench.json @@ -0,0 +1,3 @@ +{ + "parent": "wizardry:block/arcane_workbench" +} diff --git a/src/main/resources/assets/wizardry/models/item/armour_upgrade.json b/src/main/resources/assets/wizardry/models/item/armour_upgrade.json new file mode 100644 index 00000000..08fd6606 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/armour_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/armour_upgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/attunement_upgrade.json b/src/main/resources/assets/wizardry/models/item/attunement_upgrade.json new file mode 100644 index 00000000..927fd9fe --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/attunement_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/upgrade_attunement" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/basic_earth_wand.json b/src/main/resources/assets/wizardry/models/item/basic_earth_wand.json new file mode 100644 index 00000000..ddbccfee --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/basic_earth_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_basic_earth" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/basic_fire_wand.json b/src/main/resources/assets/wizardry/models/item/basic_fire_wand.json new file mode 100644 index 00000000..e323c2df --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/basic_fire_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_basic_fire" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/basic_healing_wand.json b/src/main/resources/assets/wizardry/models/item/basic_healing_wand.json new file mode 100644 index 00000000..80d6b33c --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/basic_healing_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_basic_healing" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/basic_ice_wand.json b/src/main/resources/assets/wizardry/models/item/basic_ice_wand.json new file mode 100644 index 00000000..8bd95b41 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/basic_ice_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_basic_ice" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/basic_lightning_wand.json b/src/main/resources/assets/wizardry/models/item/basic_lightning_wand.json new file mode 100644 index 00000000..2307f1a7 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/basic_lightning_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_basic_lightning" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/basic_necromancy_wand.json b/src/main/resources/assets/wizardry/models/item/basic_necromancy_wand.json new file mode 100644 index 00000000..0727fa69 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/basic_necromancy_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_basic_necromancy" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/basic_sorcery_wand.json b/src/main/resources/assets/wizardry/models/item/basic_sorcery_wand.json new file mode 100644 index 00000000..1a399976 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/basic_sorcery_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_basic_sorcery" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/blank_scroll.json b/src/main/resources/assets/wizardry/models/item/blank_scroll.json new file mode 100644 index 00000000..a6a86d05 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/blank_scroll.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/scroll" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/blast_upgrade.json b/src/main/resources/assets/wizardry/models/item/blast_upgrade.json new file mode 100644 index 00000000..3f085c0a --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/blast_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/upgrade_blast" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/condenser_upgrade.json b/src/main/resources/assets/wizardry/models/item/condenser_upgrade.json new file mode 100644 index 00000000..42a4c63f --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/condenser_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/upgrade_condenser" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/cooldown_upgrade.json b/src/main/resources/assets/wizardry/models/item/cooldown_upgrade.json new file mode 100644 index 00000000..23addd92 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/cooldown_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/upgrade_cooldown" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/crystal_block.json b/src/main/resources/assets/wizardry/models/item/crystal_block.json new file mode 100644 index 00000000..290f0923 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/crystal_block.json @@ -0,0 +1,3 @@ +{ + "parent": "wizardry:block/crystal_block" +} diff --git a/src/main/resources/assets/wizardry/models/item/crystal_flower.json b/src/main/resources/assets/wizardry/models/item/crystal_flower.json new file mode 100644 index 00000000..b71cf4d6 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/crystal_flower.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:blocks/crystal_flower" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/crystal_ore.json b/src/main/resources/assets/wizardry/models/item/crystal_ore.json new file mode 100644 index 00000000..f288786d --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/crystal_ore.json @@ -0,0 +1,3 @@ +{ + "parent": "wizardry:block/crystal_ore" +} diff --git a/src/main/resources/assets/wizardry/models/item/duration_upgrade.json b/src/main/resources/assets/wizardry/models/item/duration_upgrade.json new file mode 100644 index 00000000..5acea158 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/duration_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/upgrade_duration" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/firebomb.json b/src/main/resources/assets/wizardry/models/item/firebomb.json new file mode 100644 index 00000000..a1c8a29f --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/firebomb.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/firebomb" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/flaming_axe.json b/src/main/resources/assets/wizardry/models/item/flaming_axe.json new file mode 100644 index 00000000..104d511f --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/flaming_axe.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/flaming_axe" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/frost_axe.json b/src/main/resources/assets/wizardry/models/item/frost_axe.json new file mode 100644 index 00000000..1204aac7 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/frost_axe.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/frost_axe" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/ice_statue.json b/src/main/resources/assets/wizardry/models/item/ice_statue.json new file mode 100644 index 00000000..a3cfbe68 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/ice_statue.json @@ -0,0 +1,3 @@ +{ + "parent": "wizardry:block/ice_statue" +} diff --git a/src/main/resources/assets/wizardry/models/item/identification_scroll.json b/src/main/resources/assets/wizardry/models/item/identification_scroll.json new file mode 100644 index 00000000..4c39a408 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/identification_scroll.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/identification_scroll" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/magic_crystal.json b/src/main/resources/assets/wizardry/models/item/magic_crystal.json new file mode 100644 index 00000000..1aae8ddb --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/magic_crystal.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/magic_crystal" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/magic_light.json b/src/main/resources/assets/wizardry/models/item/magic_light.json new file mode 100644 index 00000000..c5a50b1a --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/magic_light.json @@ -0,0 +1,3 @@ +{ + "parent": "wizardry:block/sea_lantern" +} diff --git a/src/main/resources/assets/wizardry/models/item/magic_silk.json b/src/main/resources/assets/wizardry/models/item/magic_silk.json new file mode 100644 index 00000000..cec4cb55 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/magic_silk.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/magic_silk" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/magic_wand.json b/src/main/resources/assets/wizardry/models/item/magic_wand.json new file mode 100644 index 00000000..1bb79f4f --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/magic_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_basic" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/mana_flask.json b/src/main/resources/assets/wizardry/models/item/mana_flask.json new file mode 100644 index 00000000..f898986f --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/mana_flask.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/mana_flask" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/master_earth_wand.json b/src/main/resources/assets/wizardry/models/item/master_earth_wand.json new file mode 100644 index 00000000..a880c440 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/master_earth_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_master_earth" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/master_fire_wand.json b/src/main/resources/assets/wizardry/models/item/master_fire_wand.json new file mode 100644 index 00000000..f2da76dc --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/master_fire_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_master_fire" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/master_healing_wand.json b/src/main/resources/assets/wizardry/models/item/master_healing_wand.json new file mode 100644 index 00000000..17c1dd41 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/master_healing_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_master_healing" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/master_ice_wand.json b/src/main/resources/assets/wizardry/models/item/master_ice_wand.json new file mode 100644 index 00000000..79f80cb6 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/master_ice_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_master_ice" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/master_lightning_wand.json b/src/main/resources/assets/wizardry/models/item/master_lightning_wand.json new file mode 100644 index 00000000..03e5f0ca --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/master_lightning_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_master_lightning" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/master_necromancy_wand.json b/src/main/resources/assets/wizardry/models/item/master_necromancy_wand.json new file mode 100644 index 00000000..5cc36467 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/master_necromancy_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_master_necromancy" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/master_sorcery_wand.json b/src/main/resources/assets/wizardry/models/item/master_sorcery_wand.json new file mode 100644 index 00000000..67cf62ec --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/master_sorcery_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_master_sorcery" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/master_wand.json b/src/main/resources/assets/wizardry/models/item/master_wand.json new file mode 100644 index 00000000..e7875627 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/master_wand.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/wand_master" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/meteor.json b/src/main/resources/assets/wizardry/models/item/meteor.json new file mode 100644 index 00000000..14bb6068 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/meteor.json @@ -0,0 +1,3 @@ +{ + "parent": "wizardry:block/meteor" +} diff --git a/src/main/resources/assets/wizardry/models/item/petrified_stone.json b/src/main/resources/assets/wizardry/models/item/petrified_stone.json new file mode 100644 index 00000000..68982f03 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/petrified_stone.json @@ -0,0 +1,3 @@ +{ + "parent": "block/stone" +} diff --git a/src/main/resources/assets/wizardry/models/item/poison_bomb.json b/src/main/resources/assets/wizardry/models/item/poison_bomb.json new file mode 100644 index 00000000..d4c0d24a --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/poison_bomb.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/poison_bomb" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/range_upgrade.json b/src/main/resources/assets/wizardry/models/item/range_upgrade.json new file mode 100644 index 00000000..10896bb2 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/range_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/upgrade_range" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/scroll.json b/src/main/resources/assets/wizardry/models/item/scroll.json new file mode 100644 index 00000000..a6a86d05 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/scroll.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/scroll" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/siphon_upgrade.json b/src/main/resources/assets/wizardry/models/item/siphon_upgrade.json new file mode 100644 index 00000000..eec82d86 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/siphon_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/upgrade_siphon" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/smoke_bomb.json b/src/main/resources/assets/wizardry/models/item/smoke_bomb.json new file mode 100644 index 00000000..0fffc501 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/smoke_bomb.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/smoke_bomb" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/snare.json b/src/main/resources/assets/wizardry/models/item/snare.json new file mode 100644 index 00000000..ec41cce6 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/snare.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:blocks/snare" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/spawn_wizard.json b/src/main/resources/assets/wizardry/models/item/spawn_wizard.json new file mode 100644 index 00000000..908f7fa0 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/spawn_wizard.json @@ -0,0 +1,7 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "items/spawn_egg", + "layer1": "items/spawn_egg_overlay" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/spectral_block.json b/src/main/resources/assets/wizardry/models/item/spectral_block.json new file mode 100644 index 00000000..9d577615 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/spectral_block.json @@ -0,0 +1,3 @@ +{ + "parent": "wizardry:block/spectral_block" +} diff --git a/src/main/resources/assets/wizardry/models/item/spectral_boots.json b/src/main/resources/assets/wizardry/models/item/spectral_boots.json new file mode 100644 index 00000000..e21ea294 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/spectral_boots.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/spectral_boots" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/spectral_bow.json b/src/main/resources/assets/wizardry/models/item/spectral_bow.json new file mode 100644 index 00000000..649b7409 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/spectral_bow.json @@ -0,0 +1,50 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/spectral_bow_standby" + }, + "display": { + "thirdperson_righthand": { + "rotation": [ -80, 260, -40 ], + "translation": [ -1, -2, 2.5 ], + "scale": [ 0.9, 0.9, 0.9 ] + }, + "thirdperson_lefthand": { + "rotation": [ -80, -280, 40 ], + "translation": [ -1, -2, 2.5 ], + "scale": [ 0.9, 0.9, 0.9 ] + }, + "firstperson_righthand": { + "rotation": [ 0, -90, 25 ], + "translation": [ 1.13, 3.2, 1.13], + "scale": [ 0.68, 0.68, 0.68 ] + }, + "firstperson_lefthand": { + "rotation": [ 0, 90, -25 ], + "translation": [ 1.13, 3.2, 1.13], + "scale": [ 0.68, 0.68, 0.68 ] + } + }, + "overrides": [ + { + "predicate": { + "pulling": 1 + }, + "model": "wizardry:item/spectral_bow_pulling_0" + }, + { + "predicate": { + "pulling": 1, + "pull": 0.65 + }, + "model": "wizardry:item/spectral_bow_pulling_1" + }, + { + "predicate": { + "pulling": 1, + "pull": 0.9 + }, + "model": "wizardry:item/spectral_bow_pulling_2" + } + ] +} diff --git a/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_0.json b/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_0.json new file mode 100644 index 00000000..94e5d474 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_0.json @@ -0,0 +1,6 @@ +{ + "parent": "item/bow", + "textures": { + "layer0": "wizardry:items/spectral_bow_pulling_0" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_1.json b/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_1.json new file mode 100644 index 00000000..1c711fa3 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_1.json @@ -0,0 +1,6 @@ +{ + "parent": "item/bow", + "textures": { + "layer0": "wizardry:items/spectral_bow_pulling_1" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_2.json b/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_2.json new file mode 100644 index 00000000..b4762523 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_2.json @@ -0,0 +1,6 @@ +{ + "parent": "item/bow", + "textures": { + "layer0": "wizardry:items/spectral_bow_pulling_2" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/spectral_chestplate.json b/src/main/resources/assets/wizardry/models/item/spectral_chestplate.json new file mode 100644 index 00000000..33de309e --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/spectral_chestplate.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/spectral_chestplate" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/spectral_helmet.json b/src/main/resources/assets/wizardry/models/item/spectral_helmet.json new file mode 100644 index 00000000..5a8a9433 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/spectral_helmet.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/spectral_helmet" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/spectral_leggings.json b/src/main/resources/assets/wizardry/models/item/spectral_leggings.json new file mode 100644 index 00000000..e55f9c23 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/spectral_leggings.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/spectral_leggings" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/spectral_pickaxe.json b/src/main/resources/assets/wizardry/models/item/spectral_pickaxe.json new file mode 100644 index 00000000..45347c00 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/spectral_pickaxe.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/spectral_pickaxe" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/spectral_sword.json b/src/main/resources/assets/wizardry/models/item/spectral_sword.json new file mode 100644 index 00000000..4e88c159 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/spectral_sword.json @@ -0,0 +1,6 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "wizardry:items/spectral_sword" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/spell_book.json b/src/main/resources/assets/wizardry/models/item/spell_book.json new file mode 100644 index 00000000..5a3d2fdb --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/spell_book.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/spell_book" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/storage_upgrade.json b/src/main/resources/assets/wizardry/models/item/storage_upgrade.json new file mode 100644 index 00000000..3673fa43 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/storage_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/upgrade_storage" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/transportation_stone.json b/src/main/resources/assets/wizardry/models/item/transportation_stone.json new file mode 100644 index 00000000..2b0a1e09 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/transportation_stone.json @@ -0,0 +1,3 @@ +{ + "parent": "wizardry:block/transportation_stone" +} diff --git a/src/main/resources/assets/wizardry/models/item/vanishing_cobweb.json b/src/main/resources/assets/wizardry/models/item/vanishing_cobweb.json new file mode 100644 index 00000000..1fe6d579 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/vanishing_cobweb.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "blocks/web" + } +} diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots.json b/src/main/resources/assets/wizardry/models/item/wizard_boots.json new file mode 100644 index 00000000..39685945 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_boots.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_boots" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots_earth.json b/src/main/resources/assets/wizardry/models/item/wizard_boots_earth.json new file mode 100644 index 00000000..9ee903af --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_boots_earth.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_boots_earth" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots_fire.json b/src/main/resources/assets/wizardry/models/item/wizard_boots_fire.json new file mode 100644 index 00000000..a1e664d0 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_boots_fire.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_boots_fire" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots_healing.json b/src/main/resources/assets/wizardry/models/item/wizard_boots_healing.json new file mode 100644 index 00000000..57c5b635 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_boots_healing.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_boots_healing" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots_ice.json b/src/main/resources/assets/wizardry/models/item/wizard_boots_ice.json new file mode 100644 index 00000000..be14210e --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_boots_ice.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_boots_ice" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots_lightning.json b/src/main/resources/assets/wizardry/models/item/wizard_boots_lightning.json new file mode 100644 index 00000000..d6904206 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_boots_lightning.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_boots_lightning" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots_necromancy.json b/src/main/resources/assets/wizardry/models/item/wizard_boots_necromancy.json new file mode 100644 index 00000000..65abcd7b --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_boots_necromancy.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_boots_necromancy" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots_sorcery.json b/src/main/resources/assets/wizardry/models/item/wizard_boots_sorcery.json new file mode 100644 index 00000000..ffbf448b --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_boots_sorcery.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_boots_sorcery" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_handbook.json b/src/main/resources/assets/wizardry/models/item/wizard_handbook.json new file mode 100644 index 00000000..c302b6b6 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_handbook.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_handbook" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat.json b/src/main/resources/assets/wizardry/models/item/wizard_hat.json new file mode 100644 index 00000000..7f1dad7a --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_hat.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_hat" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat_earth.json b/src/main/resources/assets/wizardry/models/item/wizard_hat_earth.json new file mode 100644 index 00000000..95273a7f --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_hat_earth.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_hat_earth" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat_fire.json b/src/main/resources/assets/wizardry/models/item/wizard_hat_fire.json new file mode 100644 index 00000000..abecf19d --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_hat_fire.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_hat_fire" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat_healing.json b/src/main/resources/assets/wizardry/models/item/wizard_hat_healing.json new file mode 100644 index 00000000..0c453f01 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_hat_healing.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_hat_healing" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat_ice.json b/src/main/resources/assets/wizardry/models/item/wizard_hat_ice.json new file mode 100644 index 00000000..98430ed6 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_hat_ice.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_hat_ice" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat_lightning.json b/src/main/resources/assets/wizardry/models/item/wizard_hat_lightning.json new file mode 100644 index 00000000..408eae50 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_hat_lightning.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_hat_lightning" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat_necromancy.json b/src/main/resources/assets/wizardry/models/item/wizard_hat_necromancy.json new file mode 100644 index 00000000..8c62b1dd --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_hat_necromancy.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_hat_necromancy" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat_sorcery.json b/src/main/resources/assets/wizardry/models/item/wizard_hat_sorcery.json new file mode 100644 index 00000000..3cb674bb --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_hat_sorcery.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_hat_sorcery" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings.json new file mode 100644 index 00000000..02aed97c --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_leggings.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_leggings" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings_earth.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings_earth.json new file mode 100644 index 00000000..8fcec98f --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_leggings_earth.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_leggings_earth" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings_fire.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings_fire.json new file mode 100644 index 00000000..bcb63bac --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_leggings_fire.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_leggings_fire" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings_healing.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings_healing.json new file mode 100644 index 00000000..16c90a84 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_leggings_healing.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_leggings_healing" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings_ice.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings_ice.json new file mode 100644 index 00000000..c1424913 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_leggings_ice.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_leggings_ice" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings_lightning.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings_lightning.json new file mode 100644 index 00000000..f8e4824c --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_leggings_lightning.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_leggings_lightning" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings_necromancy.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings_necromancy.json new file mode 100644 index 00000000..9c97d209 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_leggings_necromancy.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_leggings_necromancy" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings_sorcery.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings_sorcery.json new file mode 100644 index 00000000..3ac6dd52 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_leggings_sorcery.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_leggings_sorcery" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe.json b/src/main/resources/assets/wizardry/models/item/wizard_robe.json new file mode 100644 index 00000000..7f8e266e --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_robe.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_robe" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe_earth.json b/src/main/resources/assets/wizardry/models/item/wizard_robe_earth.json new file mode 100644 index 00000000..66ed7826 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_robe_earth.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_robe_earth" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe_fire.json b/src/main/resources/assets/wizardry/models/item/wizard_robe_fire.json new file mode 100644 index 00000000..6ab282f2 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_robe_fire.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_robe_fire" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe_healing.json b/src/main/resources/assets/wizardry/models/item/wizard_robe_healing.json new file mode 100644 index 00000000..54cba900 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_robe_healing.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_robe_healing" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe_ice.json b/src/main/resources/assets/wizardry/models/item/wizard_robe_ice.json new file mode 100644 index 00000000..45398f00 --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_robe_ice.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_robe_ice" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe_lightning.json b/src/main/resources/assets/wizardry/models/item/wizard_robe_lightning.json new file mode 100644 index 00000000..ce513b4a --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_robe_lightning.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_robe_lightning" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe_necromancy.json b/src/main/resources/assets/wizardry/models/item/wizard_robe_necromancy.json new file mode 100644 index 00000000..c3ff763a --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_robe_necromancy.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_robe_necromancy" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe_sorcery.json b/src/main/resources/assets/wizardry/models/item/wizard_robe_sorcery.json new file mode 100644 index 00000000..2b5224eb --- /dev/null +++ b/src/main/resources/assets/wizardry/models/item/wizard_robe_sorcery.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "wizardry:items/wizard_robe_sorcery" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/sounds.json b/src/main/resources/assets/wizardry/sounds.json new file mode 100644 index 00000000..857b9a40 --- /dev/null +++ b/src/main/resources/assets/wizardry/sounds.json @@ -0,0 +1,39 @@ +{ + "arc": {"category": "player","sounds": [ + {"name": "wizardry:arc1","stream": false}, + {"name": "wizardry:arc1","stream": false}, + {"name": "wizardry:arc3","stream": false} + ]}, + + "aura": {"category": "player","sounds": [{"name": "wizardry:aura","stream": false}]}, + "boom": {"category": "player","sounds": [{"name": "wizardry:boom","stream": false}]}, + "crackle": {"category": "player","sounds": [{"name": "wizardry:crackle","stream": false}]}, + + "darkaura": {"category": "player","sounds": [ + {"name": "wizardry:darkaura1","stream": false}, + {"name": "wizardry:darkaura2","stream": false} + ]}, + + "effect": {"category": "player","sounds": [ + {"name": "wizardry:effect1","stream": false}, + {"name": "wizardry:effect2","stream": false} + ]}, + + "electricitya": {"category": "player","sounds": [{"name": "wizardry:electricitya","stream": false}]}, + "electricityb": {"category": "player","sounds": [{"name": "wizardry:electricityb","stream": false}]}, + + "flameray": {"category": "player","sounds": [{"name": "wizardry:flameray2","stream": false}]}, + "force": {"category": "player","sounds": [{"name": "wizardry:force","stream": false}]}, + + "freeze": {"category": "player","sounds": [{"name": "wizardry:freeze","stream": false}]}, + "frostray": {"category": "player","sounds": [{"name": "wizardry:frostray","stream": false}]}, + "heal": {"category": "player","sounds": [{"name": "wizardry:heal","stream": false}]}, + "ice": {"category": "player","sounds": [{"name": "wizardry:ice","stream": false}]}, + "largeaura": {"category": "player","sounds": [{"name": "wizardry:largeaura","stream": false}]}, + + "magic": {"category": "player","sounds": [{"name": "wizardry:magic1","stream": false}]}, + + "sparkle": {"category": "player","sounds": [{"name": "wizardry:sparkle","stream": false}]}, + "wind": {"category": "player","sounds": [{"name": "wizardry:wind","stream": false}]}, + "rumble": {"category": "player","sounds": [{"name": "wizardry:rumble","stream": false}]} +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/sounds/arc1.ogg b/src/main/resources/assets/wizardry/sounds/arc1.ogg new file mode 100644 index 00000000..f5d3c72f Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/arc1.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/arc2.ogg b/src/main/resources/assets/wizardry/sounds/arc2.ogg new file mode 100644 index 00000000..53a04e5d Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/arc2.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/arc3.ogg b/src/main/resources/assets/wizardry/sounds/arc3.ogg new file mode 100644 index 00000000..b2ee55d9 Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/arc3.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/aura.ogg b/src/main/resources/assets/wizardry/sounds/aura.ogg new file mode 100644 index 00000000..d48bdffc Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/aura.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/boom.ogg b/src/main/resources/assets/wizardry/sounds/boom.ogg new file mode 100644 index 00000000..26b3261d Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/boom.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/crackle.ogg b/src/main/resources/assets/wizardry/sounds/crackle.ogg new file mode 100644 index 00000000..cc81620e Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/crackle.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/darkaura1.ogg b/src/main/resources/assets/wizardry/sounds/darkaura1.ogg new file mode 100644 index 00000000..81b556fd Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/darkaura1.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/darkaura2.ogg b/src/main/resources/assets/wizardry/sounds/darkaura2.ogg new file mode 100644 index 00000000..df471a0d Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/darkaura2.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/effect1.ogg b/src/main/resources/assets/wizardry/sounds/effect1.ogg new file mode 100644 index 00000000..dc9a4760 Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/effect1.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/effect2.ogg b/src/main/resources/assets/wizardry/sounds/effect2.ogg new file mode 100644 index 00000000..c2c9a62d Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/effect2.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/electricitya.ogg b/src/main/resources/assets/wizardry/sounds/electricitya.ogg new file mode 100644 index 00000000..898bb0b3 Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/electricitya.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/electricityb.ogg b/src/main/resources/assets/wizardry/sounds/electricityb.ogg new file mode 100644 index 00000000..b8eb148c Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/electricityb.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/flameray1.ogg b/src/main/resources/assets/wizardry/sounds/flameray1.ogg new file mode 100644 index 00000000..587f2ac9 Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/flameray1.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/flameray2.ogg b/src/main/resources/assets/wizardry/sounds/flameray2.ogg new file mode 100644 index 00000000..1f556a3b Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/flameray2.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/force.ogg b/src/main/resources/assets/wizardry/sounds/force.ogg new file mode 100644 index 00000000..0da336b2 Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/force.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/freeze.ogg b/src/main/resources/assets/wizardry/sounds/freeze.ogg new file mode 100644 index 00000000..014dc1e4 Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/freeze.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/frostray.ogg b/src/main/resources/assets/wizardry/sounds/frostray.ogg new file mode 100644 index 00000000..0567212e Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/frostray.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/heal.ogg b/src/main/resources/assets/wizardry/sounds/heal.ogg new file mode 100644 index 00000000..a58b11f6 Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/heal.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/ice.ogg b/src/main/resources/assets/wizardry/sounds/ice.ogg new file mode 100644 index 00000000..9879c888 Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/ice.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/largeaura.ogg b/src/main/resources/assets/wizardry/sounds/largeaura.ogg new file mode 100644 index 00000000..c9caa43f Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/largeaura.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/magic1.ogg b/src/main/resources/assets/wizardry/sounds/magic1.ogg new file mode 100644 index 00000000..94b13b16 Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/magic1.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/magic2.ogg b/src/main/resources/assets/wizardry/sounds/magic2.ogg new file mode 100644 index 00000000..cf4f5a79 Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/magic2.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/magic3.ogg b/src/main/resources/assets/wizardry/sounds/magic3.ogg new file mode 100644 index 00000000..f65f2ad4 Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/magic3.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/magic4.ogg b/src/main/resources/assets/wizardry/sounds/magic4.ogg new file mode 100644 index 00000000..1793567a Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/magic4.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/rumble.ogg b/src/main/resources/assets/wizardry/sounds/rumble.ogg new file mode 100644 index 00000000..b9d208b1 Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/rumble.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/sparkle.ogg b/src/main/resources/assets/wizardry/sounds/sparkle.ogg new file mode 100644 index 00000000..e6899fee Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/sparkle.ogg differ diff --git a/src/main/resources/assets/wizardry/sounds/wind.ogg b/src/main/resources/assets/wizardry/sounds/wind.ogg new file mode 100644 index 00000000..1b824691 Binary files /dev/null and b/src/main/resources/assets/wizardry/sounds/wind.ogg differ diff --git a/src/main/resources/assets/wizardry/texts/handbook_en_GB.txt b/src/main/resources/assets/wizardry/texts/handbook_en_GB.txt new file mode 100644 index 00000000..b2c9d08a --- /dev/null +++ b/src/main/resources/assets/wizardry/texts/handbook_en_GB.txt @@ -0,0 +1,217 @@ +PAGEBREAK +LINEBREAK + + + + +The Wizard's Handbook + + + +By Electroblob +PAGEBREAK +Introduction +-------------------- + +Greetings, wizard! This book explains the many ways of the arcane and how to use them. + +Should you ever lose this book, you can get a new one by crafting a book with a magic crystal. +PAGEBREAK +Contents +-------------------- +PAGEBREAK +SECTION Setting up +Setting up +-------------------- +To get started with wizardry, you will need a few things: + +- A magic wand, crafted with a gold nugget, a stick, and a magic crystal. + +- An arcane workbench, crafted with 3 stone, 1 lapis lazuli block, 2 magic crystals, 2 gold nuggets and 1 purple carpet. + +- A spell book. These can be found in chests, dropped as loot, purchased from wizards, or you can make a beginner's spell, magic missile, with a book and 4 magic crystals. + +You will also need a bunch more magic crystals to supply your wand with mana. +PAGEBREAK +SECTION Mana +Mana +-------------------- +Mana is the arcane energy that gives wizards their powers. It manifests itself in naturally occurring crystals, which can be found embedded in rocks underground. Wands can store mana within them, and this mana is channeled into the spells that you cast. Different spells cost different amounts of mana, depending on how powerful they are and how long they last. + +Mouse over a wand to see how much mana is stored in it. +IMAGE CRYSTAL +LINEBREAK + + + + + + + Magic + Crystal Ore Crystal +PAGEBREAK +SECTION Wands +Wands +-------------------- +The wand is the implement of choice for a wizard. With it, you can cast any spell provided the wand can contain its power (see Tiers for a more in-depth explanation). Wands come in +various different varieties but you will almost certainly start with a basic magic wand. + +When holding a wand, a small heads-up display will show in the corner of the screen. This indicates the spell that is currently selected along with a pictorial representation of it, and, if the spell has been cast, a cooldown bar indicating the time until that spell can be cast again. +PAGEBREAK +SECTION Spells +Spells +-------------------- +A wand is useless without spells to cast with it. Spells are recorded in two forms: in books, which are permanent, and in scrolls, which are temporary. + +Spell books are of little use on their own; instead they are used to bind the spell they contain to wands. Spell books can be found throughout the world and it will not take long for you to come across one. When you do find one, you can mouse over the spell book to see basic information about the spell at a glance. You can also right-click whilst holding a spell book to read more detailed information about the spell. + +Scrolls, on the other hand, serve a different purpose: right-clicking whilst holding one will cast the spell that is bound to it, destroying the scroll in the process. Like spell books, scrolls can be found throughout the world, but you can also make them yourself by crafting a blank scroll from a piece of paper and some string. You can then bind a spell to the scroll using the arcane workbench, but only if you have knowledge of that spell. Doing so will consume a number of magic crystals equal to the mana cost of the spell (rounded up to the nearest MANA_PER_CRYSTAL). + +To the untrained eye, the magical runes with which spells are written are unreadable. In order to identify a spell, you could, of course, cast it and see what happens - though doing so may cause unwanted side-effects, and many spells need certain conditions in order to work. A safer and more reliable option is to use a scroll of identification, which can be found in chests or purchased from a wizard. Right- -clicking whilst holding one will identify the first unknown spell book or scroll on your hotbar, consuming the scroll of identification. + +Scrolls are quite useful when you need to use a spell once or twice on a particular quest but don't want it to take up a slot on your wand. It should be noted, however, that scrolls are at best an inefficient method of casting spells. +PAGEBREAK +SECTION Arcane Workbench +The Arcane Workbench +-------------------- +The arcane workbench is a block similar in appearance to the enchantment table where you can charge, upgrade, and bind spells to your wand. Simply place it anywhere and right click, and you will see a gui appear like the one to the right. +PAGEBREAK +IMAGE WORKBENCH +LINEBREAK + + + + + + + + + + + + + + The Arcane Workbench +PAGEBREAK +SECTION Binding Spells +Binding Spells +-------------------- +To bind a spell to your wand, simply place your wand in the central slot of the workbench, then place the spell book in one of the surrounding slots and press apply. You can bind up to five spells to a wand at one time, though this may be increased with attunement upgrades. When holding a wand, you can switch between spells with the NEXT_SPELL_KEY and PREVIOUS_SPELL_KEY keys (these can be changed in options -> controls). You can also switch spells by scrolling the mouse wheel while sneaking. + +SECTION Charging Wands +Charging your wand +-------------------- +To charge your wand, place the wand in the central slot of the arcane workbench and place some magic crystals in the upper of the two slots on the left, and then press apply. Each crystal is worth MANA_PER_CRYSTAL mana, and the wand will only take what it needs, so you can keep a supply of crystals in the workbench for when you need them. However, bear in mind that the wand can only take a whole number of crystals so if the wand needs, for example, 30 more mana, MANA_PER_CRYSTAL_MINUS_30 mana will be lost when charging it. All wands will take an exact number of crystals to charge if they are empty, unless they have a storage upgrade applied. + +SECTION Upgrading Wands +Upgrading your wand +-------------------- +To upgrade your wand, you will need a tome of arcana of the appropriate tier (see Tiers) or a special wand upgrade. Both of these can be found as loot or purchased from a wizard. To upgrade your wand, place it in the central slot and the upgrade in the lower of the two slots on the left, then press apply. + +Special wand upgrades improve a particular aspect of a wand. Each type of upgrade can stack up to three times, and the total number of upgrades that a wand can take depends on its tier. Upgrades cannot be removed. +PAGEBREAK +SECTION Flowers and Flasks +Flowers and Flasks +-------------------- +You will probably come across curious glowing flowers growing in the wild from time to time. These are crystal flowers, a naturally occurring source of mana. They can be harvested and crafted to extract the mana as crystals. + +Need to recharge your wand on the go? No problem! Mana can now be bottled. Simply craft a mana flask with a glass bottle and eight magic crystals and take it with you. When you need to use it, simply craft it with your wand and it will restore some of the charge. This process will consume the bottle. Some mana is lost during bottling. +PAGEBREAK +SECTION Tiers +Tiers +-------------------- +Wands and spells come in four tiers: BASIC_COLOURnoviceRESET_COLOUR, APPRENTICE_COLOURapprenticeRESET_COLOUR, ADVANCED_COLOURadvanced RESET_COLOURand MASTER_COLOURmasterRESET_COLOUR. Each tier is more powerful than the last. + +BASIC_COLOURNovice RESET_COLOURwands are the ones that can be crafted. They hold up to BASIC_MAX_CHARGE mana, and can only cast novice spells. Novice spells are simple enough to be cast by anyone and they do not usually cost much mana. This does not necessarily mean that they are useless at higher tiers however; their low cost makes some novice spells useful even when you have access to master spells. + +APPRENTICE_COLOURApprentice RESET_COLOURwands are the next tier up from novice wands. They hold up to APPRENTICE_MAX_CHARGE mana and can cast novice and apprentice spells. Apprentice spells are a little more impressive than novice spells but usually cost more. + +ADVANCED_COLOURAdvanced RESET_COLOURwands are quite rare and are much more powerful. They can cast all spells except master spells, and hold ADVANCED_MAX_CHARGE mana. Advanced spells can grant superhuman powers such as invisibility and wreak havoc on enemies. + +MASTER_COLOURMaster RESET_COLOURwands are the most powerful wands in existence. They hold up to MASTER_MAX_CHARGE mana, and can cast any spell. Master spell books are very rare and the spells can cause complete devastation not only to enemies but even the world itself. Use with caution. +PAGEBREAK +SECTION Elements +Elements +-------------------- +Spells belong to different elements, which describe the nature of the spell and also have perks when used with certain wands. + +FIRE_COLOURFire +Perhaps the most destructive element, fire concerns burning, lava, and explosions. A powerful pyromancer will unleash hell upon their enemies and probably set the world alight in the process. Most fire attack spells set fire to their target, causing ongoing damage over time. However, be wary that fire spells may have no effect on nether mobs. + +ICE_COLOURIce +The element of ice is made of all that is cold. Frost spells often slow enemies down or freeze them completely, and are especially effective on creatures of fire. Frost magic can prove equally useful outside of combat, such as freezing water to cross a river. + +LIGHTNING_COLOURLightning +This element concerns lightning, storms, and the weather. A powerful storm mage is a force to be reckoned with, and some posess the ability to call down lightning at will. Lightning spells often damage multiple enemies at once and usually seek their target, making them an effective attack against any mob... but beware of creepers. + +NECROMANCY_COLOURNecromancy +Necromancy is the element of darkness, chaos and the undead. Necromancers are mysterious and often regarded as evil, though this is usually not the case. Necromancy spells are commonly used to summon creatures to fight for you or even bend the will of your enemies. + +EARTH_COLOUREarth +The element of earth is focused on the natural world: animals, plants, the wind, and such like. Earth magic is diverse and takes a wide variety of forms, from poisoning enemies to unleashing the fury of the weather. Earth spells are a mixture of attack, defence and utility. + +SORCERY_COLOURSorcery +Sorcery is the element of force and change. Sorcerers manipulate light, gravity and even reality itself to fit their needs. Sorcery spells can be used, among other things, to grant their caster magical powers or move objects to their will. + +HEALING_COLOURHealing +The element of healing is concerned with defence and regeneration. Healers seek to protect themselves and their allies as much as possible and as such can be very difficult opponents. Though generally not used as an attack, the purifying light of some healing spells will do considerable damage to the undead. Healing spells are indispensable when in combat. + +The boundaries between the different elements are not always distinct, and some spells bear traits of elements other than their own. + +If you are lucky, you may happen upon an elemental wand. These wands will allow you to cast spells of the right element for more potency than usual. +PAGEBREAK +SECTION Wizard Armour +Wizard Armour +-------------------- +As a wizard, you will need something to protect you from the creatures you fight. No ordinary armour will do though. To make the most of your spells, you will need wizard armour: hat, robes, leggings and boots. Unlike ordinary armour, these will not break. Instead, they use arcane energy to shield the wearer. This of course means that they must be charged with magic crystals, just like wands. Fortunately, these garments do not consume much mana. Charge them as you would a wand in the arcane workbench or with a mana flask, but be careful - if they run out of mana, you will find yourself defenceless. + +You can obtain wizard armour by crafting it from magical silk, obtained by crafting string with a magic crystal. + +Those wizards who devote themselves to the practice of one element sometimes wear special garments which grant bonuses for that element. Full sets of such garments are very sought after among wizards and are not easy to find. + +Legend speaks of an arcane seal used by the most powerful wizards to greatly improve their armour. +PAGEBREAK +SECTION Wizards and Towers +Wizards and Towers +-------------------- +Fear not - you are not the only practicioner of magic in this world. On your travels, you may well encounter tall towers with distinctive pointed roofs. These are the residences of fellow wizards. A life of solitude can make wizards a little grumpy at times, but they are usually friendly folk who are willing to share their knowledge with you - albeit for a price. Expect to pay precious metals and gems, and in return you will recieve many a great arcane wonder. If, however, it is a master spell you seek, you will need to speak to a specialist. + +Unfortunately, there are a few wizards out there who do not welcome visitors. These wizards are typically outcasts, and are hostile to anyone crossing their path. Approach these individuals with caution, and be prepared to defend yourself against powerful magic. Defeat them however, and their knowledge is yours for the taking. +PAGEBREAK +SECTION Crafting Recipes +Crafting Recipes +-------------------- +PAGEBREAK +PAGEBREAK +Crafting Recipes +-------------------- +PAGEBREAK +PAGEBREAK +Crafting Recipes +-------------------- +PAGEBREAK +PAGEBREAK +Crafting Recipes +-------------------- +PAGEBREAK +PAGEBREAK +Credits +-------------------- +Electroblob's Wizardry +Version VERSION +For Minecraft 1.10.2 + +Designed, coded and textured by Electroblob + +Thanks to Minecraft Forge and MCP, without which this mod would not have been possible. + +Thanks also to the Minecraft modding community, which always has an answer to my modding problems! + +In addition, I'd like to thank the following individuals for their contributions to the mod: + +Translations: + +- Russian: VilagVil +- Spanish and Mexican Spanish: MadWrist + +Lightning ray sound effect from OhhWowProductions \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/texts/handbook_en_US.txt b/src/main/resources/assets/wizardry/texts/handbook_en_US.txt new file mode 100644 index 00000000..95746130 --- /dev/null +++ b/src/main/resources/assets/wizardry/texts/handbook_en_US.txt @@ -0,0 +1,217 @@ +PAGEBREAK +LINEBREAK + + + + +The Wizard's Handbook + + + +By Electroblob +PAGEBREAK +Introduction +-------------------- + +Greetings, wizard! This book explains the many ways of the arcane and how to use them. + +Should you ever lose this book, you can get a new one by crafting a book with a magic crystal. +PAGEBREAK +Contents +-------------------- +PAGEBREAK +SECTION Setting up +Setting up +-------------------- +To get started with wizardry, you will need a few things: + +- A magic wand, crafted with a gold nugget, a stick, and a magic crystal. + +- An arcane workbench, crafted with 3 stone, 1 lapis lazuli block, 2 magic crystals, 2 gold nuggets and 1 purple carpet. + +- A spell book. These can be found in chests, dropped as loot, purchased from wizards, or you can make a beginner's spell, magic missile, with a book and 4 magic crystals. + +You will also need a bunch more magic crystals to supply your wand with mana. +PAGEBREAK +SECTION Mana +Mana +-------------------- +Mana is the arcane energy that gives wizards their powers. It manifests itself in naturally occurring crystals, which can be found embedded in rocks underground. Wands can store mana within them, and this mana is channeled into the spells that you cast. Different spells cost different amounts of mana, depending on how powerful they are and how long they last. + +Mouse over a wand to see how much mana is stored in it. +IMAGE CRYSTAL +LINEBREAK + + + + + + + Magic + Crystal Ore Crystal +PAGEBREAK +SECTION Wands +Wands +-------------------- +The wand is the implement of choice for a wizard. With it, you can cast any spell provided the wand can contain its power (see Tiers for a more in-depth explanation). Wands come in +various different varieties but you will almost certainly start with a basic magic wand. + +When holding a wand, a small heads-up display will show in the corner of the screen. This indicates the spell that is currently selected along with a pictorial representation of it, and, if the spell has been cast, a cooldown bar indicating the time until that spell can be cast again. +PAGEBREAK +SECTION Spells +Spells +-------------------- +A wand is useless without spells to cast with it. Spells are recorded in two forms: in books, which are permanent, and in scrolls, which are temporary. + +Spell books are of little use on their own; instead they are used to bind the spell they contain to wands. Spell books can be found throughout the world and it will not take long for you to come across one. When you do find one, you can mouse over the spell book to see basic information about the spell at a glance. You can also right-click whilst holding a spell book to read more detailed information about the spell. + +Scrolls, on the other hand, serve a different purpose: right-clicking whilst holding one will cast the spell that is bound to it, destroying the scroll in the process. Like spell books, scrolls can be found throughout the world, but you can also make them yourself by crafting a blank scroll from a piece of paper and some string. You can then bind a spell to the scroll using the arcane workbench, but only if you have knowledge of that spell. Doing so will consume a number of magic crystals equal to the mana cost of the spell (rounded up to the nearest MANA_PER_CRYSTAL). + +To the untrained eye, the magical runes with which spells are written are unreadable. In order to identify a spell, you could, of course, cast it and see what happens - though doing so may cause unwanted side-effects, and many spells need certain conditions in order to work. A safer and more reliable option is to use a scroll of identification, which can be found in chests or purchased from a wizard. Right- -clicking whilst holding one will identify the first unknown spell book or scroll on your hotbar, consuming the scroll of identification. + +Scrolls are quite useful when you need to use a spell once or twice on a particular quest but don't want it to take up a slot on your wand. It should be noted, however, that scrolls are at best an inefficient method of casting spells. +PAGEBREAK +SECTION Arcane Workbench +The Arcane Workbench +-------------------- +The arcane workbench is a block similar in appearance to the enchantment table where you can charge, upgrade, and bind spells to your wand. Simply place it anywhere and right click, and you will see a gui appear like the one to the right. +PAGEBREAK +IMAGE WORKBENCH +LINEBREAK + + + + + + + + + + + + + + The Arcane Workbench +PAGEBREAK +SECTION Binding Spells +Binding Spells +-------------------- +To bind a spell to your wand, simply place your wand in the central slot of the workbench, then place the spell book in one of the surrounding slots and press apply. You can bind up to five spells to a wand at one time, though this may be increased with attunement upgrades. When holding a wand, you can switch between spells with the NEXT_SPELL_KEY and PREVIOUS_SPELL_KEY keys (these can be changed in options -> controls). You can also switch spells by scrolling the mouse wheel while sneaking. + +SECTION Charging Wands +Charging your wand +-------------------- +To charge your wand, place the wand in the central slot of the arcane workbench and place some magic crystals in the upper of the two slots on the left, and then press apply. Each crystal is worth MANA_PER_CRYSTAL mana, and the wand will only take what it needs, so you can keep a supply of crystals in the workbench for when you need them. However, bear in mind that the wand can only take a whole number of crystals so if the wand needs, for example, 30 more mana, MANA_PER_CRYSTAL_MINUS_30 mana will be lost when charging it. All wands will take an exact number of crystals to charge if they are empty, unless they have a storage upgrade applied. + +SECTION Upgrading Wands +Upgrading your wand +-------------------- +To upgrade your wand, you will need a tome of arcana of the appropriate tier (see Tiers) or a special wand upgrade. Both of these can be found as loot or purchased from a wizard. To upgrade your wand, place it in the central slot and the upgrade in the lower of the two slots on the left, then press apply. + +Special wand upgrades improve a particular aspect of a wand. Each type of upgrade can stack up to three times, and the total number of upgrades that a wand can take depends on its tier. Upgrades cannot be removed. +PAGEBREAK +SECTION Flowers and Flasks +Flowers and Flasks +-------------------- +You will probably come across curious glowing flowers growing in the wild from time to time. These are crystal flowers, a naturally occurring source of mana. They can be harvested and crafted to extract the mana as crystals. + +Need to recharge your wand on the go? No problem! Mana can now be bottled. Simply craft a mana flask with a glass bottle and eight magic crystals and take it with you. When you need to use it, simply craft it with your wand and it will restore some of the charge. This process will consume the bottle. Some mana is lost during bottling. +PAGEBREAK +SECTION Tiers +Tiers +-------------------- +Wands and spells come in four tiers: BASIC_COLOURnoviceRESET_COLOUR, APPRENTICE_COLOURapprenticeRESET_COLOUR, ADVANCED_COLOURadvanced RESET_COLOURand MASTER_COLOURmasterRESET_COLOUR. Each tier is more powerful than the last. + +BASIC_COLOURNovice RESET_COLOURwands are the ones that can be crafted. They hold up to BASIC_MAX_CHARGE mana, and can only cast novice spells. Novice spells are simple enough to be cast by anyone and they do not usually cost much mana. This does not necessarily mean that they are useless at higher tiers however; their low cost makes some novice spells useful even when you have access to master spells. + +APPRENTICE_COLOURApprentice RESET_COLOURwands are the next tier up from novice wands. They hold up to APPRENTICE_MAX_CHARGE mana and can cast novice and apprentice spells. Apprentice spells are a little more impressive than novice spells but usually cost more. + +ADVANCED_COLOURAdvanced RESET_COLOURwands are quite rare and are much more powerful. They can cast all spells except master spells, and hold ADVANCED_MAX_CHARGE mana. Advanced spells can grant superhuman powers such as invisibility and wreak havoc on enemies. + +MASTER_COLOURMaster RESET_COLOURwands are the most powerful wands in existence. They hold up to MASTER_MAX_CHARGE mana, and can cast any spell. Master spell books are very rare and the spells can cause complete devastation not only to enemies but even the world itself. Use with caution. +PAGEBREAK +SECTION Elements +Elements +-------------------- +Spells belong to different elements, which describe the nature of the spell and also have perks when used with certain wands. + +FIRE_COLOURFire +Perhaps the most destructive element, fire concerns burning, lava, and explosions. A powerful pyromancer will unleash hell upon their enemies and probably set the world alight in the process. Most fire attack spells set fire to their target, causing ongoing damage over time. However, be wary that fire spells may have no effect on nether mobs. + +ICE_COLOURIce +The element of ice is made of all that is cold. Frost spells often slow enemies down or freeze them completely, and are especially effective on creatures of fire. Frost magic can prove equally useful outside of combat, such as freezing water to cross a river. + +LIGHTNING_COLOURLightning +This element concerns lightning, storms, and the weather. A powerful storm mage is a force to be reckoned with, and some posess the ability to call down lightning at will. Lightning spells often damage multiple enemies at once and usually seek their target, making them an effective attack against any mob... but beware of creepers. + +NECROMANCY_COLOURNecromancy +Necromancy is the element of darkness, chaos and the undead. Necromancers are mysterious and often regarded as evil, though this is usually not the case. Necromancy spells are commonly used to summon creatures to fight for you or even bend the will of your enemies. + +EARTH_COLOUREarth +The element of earth is focused on the natural world: animals, plants, the wind, and such like. Earth magic is diverse and takes a wide variety of forms, from poisoning enemies to unleashing the fury of the weather. Earth spells are a mixture of attack, defense and utility. + +SORCERY_COLOURSorcery +Sorcery is the element of force and change. Sorcerers manipulate light, gravity and even reality itself to fit their needs. Sorcery spells can be used, among other things, to grant their caster magical powers or move objects to their will. + +HEALING_COLOURHealing +The element of healing is concerned with defense and regeneration. Healers seek to protect themselves and their allies as much as possible and as such can be very difficult opponents. Though generally not used as an attack, the purifying light of some healing spells will do considerable damage to the undead. Healing spells are indispensable when in combat. + +The boundaries between the different elements are not always distinct, and some spells bear traits of elements other than their own. + +If you are lucky, you may happen upon an elemental wand. These wands will allow you to cast spells of the right element for more potency than usual. +PAGEBREAK +SECTION Wizard Armor +Wizard Armor +-------------------- +As a wizard, you will need something to protect you from the creatures you fight. No ordinary armor will do though. To make the most of your spells, you will need wizard armor: hat, robes, leggings and boots. Unlike ordinary armor, these will not break. Instead, they use arcane energy to shield the wearer. This of course means that they must be charged with magic crystals, just like wands. Fortunately, these garments do not consume much mana. Charge them as you would a wand in the arcane workbench or with a mana flask, but be careful - if they run out of mana, you will find yourself defenseless. + +You can obtain wizard armor by crafting it from magical silk, obtained by crafting string with a magic crystal. + +Those wizards who devote themselves to the practice of one element sometimes wear special garments which grant bonuses for that element. Full sets of such garments are very sought after among wizards and are not easy to find. + +Legend speaks of an arcane seal used by the most powerful wizards to greatly improve their armor. +PAGEBREAK +SECTION Wizards and Towers +Wizards and Towers +-------------------- +Fear not - you are not the only practicioner of magic in this world. On your travels, you may well encounter tall towers with distinctive pointed roofs. These are the residences of fellow wizards. A life of solitude can make wizards a little grumpy at times, but they are usually friendly folk who are willing to share their knowledge with you - albeit for a price. Expect to pay precious metals and gems, and in return you will recieve many a great arcane wonder. If, however, it is a master spell you seek, you will need to speak to a specialist. + +Unfortunately, there are a few wizards out there who do not welcome visitors. These wizards are typically outcasts, and are hostile to anyone crossing their path. Approach these individuals with caution, and be prepared to defend yourself against powerful magic. Defeat them however, and their knowledge is yours for the taking. +PAGEBREAK +SECTION Crafting Recipes +Crafting Recipes +-------------------- +PAGEBREAK +PAGEBREAK +Crafting Recipes +-------------------- +PAGEBREAK +PAGEBREAK +Crafting Recipes +-------------------- +PAGEBREAK +PAGEBREAK +Crafting Recipes +-------------------- +PAGEBREAK +PAGEBREAK +Credits +-------------------- +Electroblob's Wizardry +Version VERSION +For Minecraft 1.10.2 + +Designed, coded and textured by Electroblob + +Thanks to Minecraft Forge and MCP, without which this mod would not have been possible. + +Thanks also to the Minecraft modding community, which always has an answer to my modding problems! + +In addition, I'd like to thank the following individuals for their contributions to the mod: + +Translations: + +- Russian: VilagVil +- Spanish and Mexican Spanish: MadWrist + +Lightning ray sound effect from OhhWowProductions \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/texts/handbook_es_ES.txt b/src/main/resources/assets/wizardry/texts/handbook_es_ES.txt new file mode 100644 index 00000000..9747dc36 --- /dev/null +++ b/src/main/resources/assets/wizardry/texts/handbook_es_ES.txt @@ -0,0 +1,311 @@ +PAGEBREAK +LINEBREAK + + + + +El Manual del Mago + + + +Por Electroblob +PAGEBREAK +Introduccion +-------------------- + +Saludos, mago! Este libro explica las diferentes formas de lo arcano y como +usarlas. + +Si alguna vez pierde este libro, lo puede volver a fabricar con un libro y un cristal magico. +PAGEBREAK +Contents +-------------------- +PAGEBREAK +SECTION Comenzando +Comenzando +-------------------- +Para iniciar con Wizardry, necesitaras algunas cosas: + +- Una varita magica, crafteada con una pepita de oro, un palo y un cristal magico. + +- Una Mesa de Trabajo Arcana, crafteada con 3 piedras, 1 bloque de lapis lazuli, +2 cristales magicos, 2 pepitas de oro y 1 alfombra morada. + +- Un libro de hechizos. Estos pueden ser encontrados en cofres, arrojados como botin, +comprados a magos, o puedes hacer el hechizo del aprendiz, Misil Magico, con un +libro y 4 cristales magicos. + +Tambien necesitaras un monton de cristales magicos para suplir tu varita con mana. +PAGEBREAK +SECTION Mana +Mana +-------------------- +Mana es la energia arcana que le da a los magos sus poderes. Se manifiesta en cristales que aparecen naturalmente, los cuales aparecen incrustados bajo roca. Las varitas almacenan mana dentro de ellas, y este mana es canalizado en los hechizos que tu lanzas. Cada hechizo cuesta una cantidad diferente de mana, dependiendo de que tan +poderosos sean y que tanto duran. + +Coloca tu raton sobre una varita para ver la cantidad de mana que tiene. +IMAGE CRYSTAL +LINEBREAK + + + + + + + Mena +de Cristal Magico Cristal +PAGEBREAK +SECTION Varitas +Varitas +-------------------- +La varita es la herramienta de eleccion de un mago. Con ella, puedes lanzar cualquier +hechizo, solo si la varita contiene dicho poder (ver Nivel de varitas para entender mejor). +Las varitas vienen en variedades diferentes, es muy probable que empieces con la varita magica +basica. + +Cuando tienes una varita en tu mano, aparecera una pequena ventana en el esquina de la +pantalla. Esto indica el hechizo que esta seleccionado actualmente con una representacion +de este, y, si el hechizo ha sido lanzado, una barra con el tiempo de reactivacion para que +el hechizo pueda volcver a ser lanzado de nuevo. +PAGEBREAK +SECTION Hechizos +Hechizos +-------------------- +Una varita es inservible sin hechizos que lanzar. Los hechizos estan guardados en dos +formas: en libros, los cuales son permanentes, y en pergaminos, que son temporales. + +Los libros de hechizos son poco utiles ellos solos, por eso se suele ligar el hechizo +que contienen a las varitas. Los libros de hechizos pueden ser encontrados por todo el +mundo. Cuando encuentres uno, puedes colocar el raton sobre el libro para ver su informacion. +Tambien puede hacer clic derecho mientras lo tiene en su mano para ver mas informacion. + +Pergaminos, por otro lado, sirven un proposito diferente: hacer clic derecho con +el en la mano, lanzara el hechizo que tenga ligado a el, destruyendo el papel en el +proceso. Como los libros, los hechizos pueden ser encontrados por todo el mundo, pero +tambien puedes hacerlos tu mismo al craftear un pergamino vacio con papel y un poco de +hilo. Puedes ligarle un hechizo usando una Mesa de Trabajo Arcana, pero solo si ya conoces +dicho hechizo. Hacer esto consumira un cantidad de cristales magicos equivalente al mana +que cuesta lanzar el hechizo (redondeado al MANA_PER_CRYSTAL mas cercano). + +Para el ojo sin experiencia, las runas magicas con las cuales los hechizos son escritos +son ilegibles. Por eso, para identificar un hechizo, podrias lanzarlo y ver sus efectos +pero quizas ocasione efectos no deseados, y algunos hechizos necesitan condicione necesarias +para funcionar. Una forma mas segura, es usar un Pergamino de la Identificacion, el cual +puede ser comprado a magos. Hacer clic derecho con uno en la mano identificara el +primer hechizo desconocido en tu barra. + +Los pergaminos son utiles cuando necesitas lanzar un hechizo una o dos veces en una +ocasion en particular pero no quieres que te ocupe un espacio en tu varita. Hay que +resaltar, como sea, que los pergaminos son un metodo ineficiente de lanzar hechizos. +PAGEBREAK +SECTION Mesa de Trabajo Arcana +La Mesa de Trabajo Arcana +-------------------- +La Mesa de Trabajo Arcana es un bloque similar en apariencia a la mesa de encantamiento +donde puedes cargar, mejorar y ligar hechizos a una varita. Simplemente coloca donde sea +y haz clic derecho, y veras una interfaz aparecer parecida a la de la derecha. +PAGEBREAK +IMAGE WORKBENCH +LINEBREAK + + + + + + + + + + + + + + La Mesa de Trabajo Arcana +PAGEBREAK +SECTION Ligando Hechizos +Ligando Hechizos +-------------------- +Para ligar un hechizo a tu varita, simplemente coloca tu varita en el espacio central +de la Mesa de Trabajo Arcana, luego coloca el libro de hechizo en uno de los espacios +que estan alrededor y pulsa Aplicar. Puedes ligar 5 hechizos a la vez, esto puede ser +aumentado con mejoras. Cuando tienes una varita en la mano, puedes cambiar los hechizos +con las NEXT_SPELL_KEY y PREVIOUS_SPELL_KEY teclas. Tambien puedes cambiarlo al agacharte +y moviendo la rueda del raton. + +SECTION Cargando varitas +Cargando varitas +-------------------- +Para cargar tu varita, coloca la varita en el espacio central de la Mesa de Trabajo Arcana +y coloca algunos cristales magicos en el espacio mas arriba en la izquierda, y despues +pulsa Aplicar. Cada cristal vale MANA_PER_CRYSTAL mana, y la varita solo toma lo que +necesita, asi que puedes mantener cristales en la Mesa de Trabajo Arcana para cuando +los necesites. Todas las varitas tomaran un numero exacto de cristales para cargarla si esta +vacia, a menos que tengan una mejora de almacenamiento aplicada. + +SECTION Mejorando varitas +Mejorando tu varita +-------------------- +Para mejorar tu varita, necesitaras un Tomo de lo Arcano de tu nivel apropiado (ver +Niveles) o una mejora de varita especial. Ambos pueden ser encontrados como botin o +comprado a un mago. Para mejorar tu varita, colocala en el espacio central de la +Mesa de Trabajo Arcana y la mejora en el espacio inferior a la izquierda, y pulsar Aplicar. + +Mejoras de varita especiales mejorar un aspecto particular de la varita. Cada tipo de mejora +se puede apilar 3 veces, y el total de mejoras que puede tener una varita depende del nivel. +Las mejoras no pueden ser removidas. +PAGEBREAK +SECTION Flores y Frascos +Flores y Frascos +-------------------- +Es probable que te encuentres con flores brillantes creciendo en el suelo de tiempo +en tiempo. Esas son flores de cristal magico, una fuente de mana natural. Pueden ser +cosechadas y crafteadas para extraer mana en cristales. + +Necesitas recargar tu varita de camino? No hay problema! El mana puede ser enfrascado. +Simplemente craftea un frasco de mana con una botella de cristal y ocho cristales magicos +y llevatelo contigo. Cuando necesites mana, solo craftea la varita con el frasco para +recargar un poco. Este proceso consumira la botella. Un poco de mana se perdera en el +proceso de enfrascamiento. +PAGEBREAK +SECTION Niveles +Niveles +-------------------- +Las varitas y los hechizos vienen en cuatro niveles: BASIC_COLOURBasicoRESET_COLOUR, APPRENTICE_COLOURAprendizRESET_COLOUR, ADVANCED_COLOURAvanzadoRESET_COLOUR y MASTER_COLOURMaestroRESET_COLOUR. Cada nivel es mejor que el anterior. + +Las varitas BASIC_COLOURBasico RESET_COLOURson las que pueden ser crafteadas. Almacenan +BASIC_MAX_CHARGE mana, y solo pueden lanzar hechizos basicos. Hechizos basicos son suficientemente simples para ser lanzados por cualquiera y no usan mucho mana. Esto +no significa que son inservibles en niveles mas altos, sin embargo; su costo pequeno +hace que algunos hechizos basicos sean utiles cuando tienes acceso a hechizos maestros. + +Las varitas APPRENTICE_COLOURAprendiz RESET_COLOURson el siguiente nivel despues de las +varitas basicas. Almacenan APPRENTICE_MAX_CHARGE mana y pueden lanzar hechizos basicos y de +aprendiz. Los hechizos de aprendiz son mas llamativos que los basicos, pero cuestan mas. + +Las varitas ADVANCED_COLOURAvanzado RESET_COLOURson muy raras y mucho mas poderosas. +Pueden lanzar todos los hechizos excepto los maestros, y almacenar ADVANCED_MAX_CHARGE mana. +Los hechizos avanzados pueden otorgar poderes de superhumano como invisibilidad y causar +mucho dano a los enemigos. + +Las varitas MASTER_COLOURMaestro RESET_COLOURson las mas poderosas varitas que existen. Almacenan MASTER_MAX_CHARGE mana, y pueden lanzar cualquier hechizo. Los libros de hechizos +maestros son muy raros y los hechizos pueden causar una gran devastacion, no solo a los enemigos +si no al mundo en si. Usar con precaucion. +PAGEBREAK +SECTION Elementos +Elementos +-------------------- +Los hechizos pertenecen a diferentes elementos, los cuales describen la naturaleza +del hechizo y tambien tienen ventajas cuando son lanzados con ciertas varitas. + +FIRE_COLOURFuego +Quizas el elemento mas destructivo, fuego conlleva quemaduras, lava, y explosiones. Un piromano poderoso desatara el infierno sobre sus enemigos y probablemente encienda +el mundo en el proceso. La mayoria de los ataques de fuego queman a sus objetivos, +causando dano extra sobre el tiempo. Sin embargo, te en cuenta que no tendran efecto +en monstruos del Nether. + +ICE_COLOURHielo +El elemento de hielo esta formado por todo lo que es frio. Los hechizos de hielo +frencuentemente ralentizan a los enemigos o los congelan completamente, y son +efectivos contra criaturas de fuego. La magia de hielo puede ser util fuera de +combate, como para congelar un rio para poder cruzarlo a pie. + +LIGHTNING_COLOURRayo +Este elemento conlleva relampagos, tormentas y el clima. Un mago de tormenta poderoso +es una fuerza dura de vencer, y algunos son capaces de llamar rayos a voluntad. Los +hechizos de rayo con frecuencia hacen dano a varios enemigos a la vez y usualmente +persiguen a su objetivo, haciendolos efectivos para atacar a cualquier monstruo... +pero cuidado con los creepers. + +NECROMANCY_COLOURNigromancia +Nigromancia es el elemento de oscuridad, caos y lo no-muerto. Nigromantes son +misteriosos y a veces considerados malos, aunque no siempre es el casp. Los +hechizos de nigromancia son usados para invocar criaturas que pelearan por ti o +hasta controlar la voluntad de tus enemigos. + +EARTH_COLOURTierra +El elemento de tierra esta enfocado en el mundo natural: animales, plantas, el viento, y cosas asi. La magia de tierra es diversa y toma una gran variedad de formas, desde envenerar enemigos hasta desatar la furia del clima. Los hechizos de tierra son una liga de ataque, defensa y utilidad. + +SORCERY_COLOURHechiceria +La hechiceria es el elemento de la fuerza y el cambio. Los hechiceros manipulan la luz, +la gravedad y hasta la realidad para su beneficio. Los hechizos de hechiceria pueden +ser usados, entre otras cosas, para conceder poderes magicos y mover objetos. + +HEALING_COLOURCuracion +El elemento de curacion conlleva defensa y regeneracion. Los sanadores buscan +protegerse y a sus aliados lo mas posble, lo cual puede ser dificil para los +oponentes. A pesar de no considerarse como un ataque, la luz purificadora de sus +hechizos de curacion puede ser letales para los no-muertos. Los hechizos de +curacion son indispensables cuando se esta dentro de combate. + +Los limites entre los diferentes elementos no siempre son distintos, y algunos hechizos +tienen atributos de otros elementos mas que del suyo. + +Si eres suertudo, puedes que te encuentres una varita elemental. Estas varitas te +permiten lanzar hechizos del elemento correcto con mas potencia de lo usual. +PAGEBREAK +SECTION Armadura de Mago +Armadura de Mago +-------------------- +Como mago, necesitaras algo para protegerte de las criaturas que combates. +Ninguna armadura ordinal valdra. Para sacarle provecho a tus hechizos, necesitas +armadura de mago: sombrero, tunica, pantalones y botas. A diferencia de las otras, +estas no se romperan. Sin embargo, usaran energia arcana para proteger al usuario. +Esto significa que deben ser cargados con cristales magicos. Ventajosamente no +consumen mucho mana. Recargalos en la mesa o con frascos. + +Puedes obtener armadura de mago crafteandola con Seda Magica, obtenida al craftear +hilo con un cristal magico. + +Esos magos que se dedican a practicar un solo elemento algunas veces visten +piezas especiales las cuales ofrecen bonus a dicho elemento. Los sets completos +de esta armadura son muy codiciados entre magos y no son faciles de conseguir. + +Hay una legenda que habla sobre un sello arcano usado por los magos mas poderosos +para mejorar en sobre manera su armadura. +PAGEBREAK +SECTION Magos y Torres +Magos y Torres +-------------------- +No temas - no eres el unico que practica magia en este mundo. En tus viajes, te encontraras con torres altas con unos distintivos techos puntiagudos. +Estas son las residencias de varios magos. Una vida de soledad puede hacer que los magos se vuelvan grunones, pero usualmente son seres amigables que estan +dispuestos a compartir su conocimiento contigo - con un precio. Preparate para pagar con gemas y metales preciosos, y a cambio recibiras grandezas arcanas. +Si, en dado caso, un hechizo maestro es lo que buscas, deberar hablar con un especialista en dicho elemento. + +Desafortunadamente, hay algunos magos que no le dan la bienvenida a los visitantes. Estos magos son marginados y son hostiles hacia cualquiera que cruce su camino. Aproximate a ellos con cautela, y preparate para defenderte contra magia sumamente poderosa. Derrotalos, y su conocimiento sera tuyo. +PAGEBREAK +SECTION Recetas de Crafteo +Recetas de Crafteo +-------------------- +PAGEBREAK +PAGEBREAK +Recetas de Crafteo +-------------------- +PAGEBREAK +PAGEBREAK +Recetas de Crafteo +-------------------- +PAGEBREAK +PAGEBREAK +Recetas de Crafteo +-------------------- +PAGEBREAK +PAGEBREAK +Creditos +-------------------- +Electroblob's Wizardry +Version VERSION +Para Minecraft 1.10.2 + +Disenado, codificado y texturizado por Electroblob + +Gracias a Minecraft Forge y MCP, porque sin estos esto no hubiese sido posible. + +Gracias tambien a la comunidad de Minecraft que crea mods, la cual siempre tiene una respuesta para mis problemas! + +In addition, I'd like to thank the following individuals for their contributions to the mod: + +Translations: + +- Russian: VilagVil +- Spanish and Mexican Spanish: MadWrist + +Lightning ray sound effect from OhhWowProductions \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/texts/handbook_es_MX.txt b/src/main/resources/assets/wizardry/texts/handbook_es_MX.txt new file mode 100644 index 00000000..9747dc36 --- /dev/null +++ b/src/main/resources/assets/wizardry/texts/handbook_es_MX.txt @@ -0,0 +1,311 @@ +PAGEBREAK +LINEBREAK + + + + +El Manual del Mago + + + +Por Electroblob +PAGEBREAK +Introduccion +-------------------- + +Saludos, mago! Este libro explica las diferentes formas de lo arcano y como +usarlas. + +Si alguna vez pierde este libro, lo puede volver a fabricar con un libro y un cristal magico. +PAGEBREAK +Contents +-------------------- +PAGEBREAK +SECTION Comenzando +Comenzando +-------------------- +Para iniciar con Wizardry, necesitaras algunas cosas: + +- Una varita magica, crafteada con una pepita de oro, un palo y un cristal magico. + +- Una Mesa de Trabajo Arcana, crafteada con 3 piedras, 1 bloque de lapis lazuli, +2 cristales magicos, 2 pepitas de oro y 1 alfombra morada. + +- Un libro de hechizos. Estos pueden ser encontrados en cofres, arrojados como botin, +comprados a magos, o puedes hacer el hechizo del aprendiz, Misil Magico, con un +libro y 4 cristales magicos. + +Tambien necesitaras un monton de cristales magicos para suplir tu varita con mana. +PAGEBREAK +SECTION Mana +Mana +-------------------- +Mana es la energia arcana que le da a los magos sus poderes. Se manifiesta en cristales que aparecen naturalmente, los cuales aparecen incrustados bajo roca. Las varitas almacenan mana dentro de ellas, y este mana es canalizado en los hechizos que tu lanzas. Cada hechizo cuesta una cantidad diferente de mana, dependiendo de que tan +poderosos sean y que tanto duran. + +Coloca tu raton sobre una varita para ver la cantidad de mana que tiene. +IMAGE CRYSTAL +LINEBREAK + + + + + + + Mena +de Cristal Magico Cristal +PAGEBREAK +SECTION Varitas +Varitas +-------------------- +La varita es la herramienta de eleccion de un mago. Con ella, puedes lanzar cualquier +hechizo, solo si la varita contiene dicho poder (ver Nivel de varitas para entender mejor). +Las varitas vienen en variedades diferentes, es muy probable que empieces con la varita magica +basica. + +Cuando tienes una varita en tu mano, aparecera una pequena ventana en el esquina de la +pantalla. Esto indica el hechizo que esta seleccionado actualmente con una representacion +de este, y, si el hechizo ha sido lanzado, una barra con el tiempo de reactivacion para que +el hechizo pueda volcver a ser lanzado de nuevo. +PAGEBREAK +SECTION Hechizos +Hechizos +-------------------- +Una varita es inservible sin hechizos que lanzar. Los hechizos estan guardados en dos +formas: en libros, los cuales son permanentes, y en pergaminos, que son temporales. + +Los libros de hechizos son poco utiles ellos solos, por eso se suele ligar el hechizo +que contienen a las varitas. Los libros de hechizos pueden ser encontrados por todo el +mundo. Cuando encuentres uno, puedes colocar el raton sobre el libro para ver su informacion. +Tambien puede hacer clic derecho mientras lo tiene en su mano para ver mas informacion. + +Pergaminos, por otro lado, sirven un proposito diferente: hacer clic derecho con +el en la mano, lanzara el hechizo que tenga ligado a el, destruyendo el papel en el +proceso. Como los libros, los hechizos pueden ser encontrados por todo el mundo, pero +tambien puedes hacerlos tu mismo al craftear un pergamino vacio con papel y un poco de +hilo. Puedes ligarle un hechizo usando una Mesa de Trabajo Arcana, pero solo si ya conoces +dicho hechizo. Hacer esto consumira un cantidad de cristales magicos equivalente al mana +que cuesta lanzar el hechizo (redondeado al MANA_PER_CRYSTAL mas cercano). + +Para el ojo sin experiencia, las runas magicas con las cuales los hechizos son escritos +son ilegibles. Por eso, para identificar un hechizo, podrias lanzarlo y ver sus efectos +pero quizas ocasione efectos no deseados, y algunos hechizos necesitan condicione necesarias +para funcionar. Una forma mas segura, es usar un Pergamino de la Identificacion, el cual +puede ser comprado a magos. Hacer clic derecho con uno en la mano identificara el +primer hechizo desconocido en tu barra. + +Los pergaminos son utiles cuando necesitas lanzar un hechizo una o dos veces en una +ocasion en particular pero no quieres que te ocupe un espacio en tu varita. Hay que +resaltar, como sea, que los pergaminos son un metodo ineficiente de lanzar hechizos. +PAGEBREAK +SECTION Mesa de Trabajo Arcana +La Mesa de Trabajo Arcana +-------------------- +La Mesa de Trabajo Arcana es un bloque similar en apariencia a la mesa de encantamiento +donde puedes cargar, mejorar y ligar hechizos a una varita. Simplemente coloca donde sea +y haz clic derecho, y veras una interfaz aparecer parecida a la de la derecha. +PAGEBREAK +IMAGE WORKBENCH +LINEBREAK + + + + + + + + + + + + + + La Mesa de Trabajo Arcana +PAGEBREAK +SECTION Ligando Hechizos +Ligando Hechizos +-------------------- +Para ligar un hechizo a tu varita, simplemente coloca tu varita en el espacio central +de la Mesa de Trabajo Arcana, luego coloca el libro de hechizo en uno de los espacios +que estan alrededor y pulsa Aplicar. Puedes ligar 5 hechizos a la vez, esto puede ser +aumentado con mejoras. Cuando tienes una varita en la mano, puedes cambiar los hechizos +con las NEXT_SPELL_KEY y PREVIOUS_SPELL_KEY teclas. Tambien puedes cambiarlo al agacharte +y moviendo la rueda del raton. + +SECTION Cargando varitas +Cargando varitas +-------------------- +Para cargar tu varita, coloca la varita en el espacio central de la Mesa de Trabajo Arcana +y coloca algunos cristales magicos en el espacio mas arriba en la izquierda, y despues +pulsa Aplicar. Cada cristal vale MANA_PER_CRYSTAL mana, y la varita solo toma lo que +necesita, asi que puedes mantener cristales en la Mesa de Trabajo Arcana para cuando +los necesites. Todas las varitas tomaran un numero exacto de cristales para cargarla si esta +vacia, a menos que tengan una mejora de almacenamiento aplicada. + +SECTION Mejorando varitas +Mejorando tu varita +-------------------- +Para mejorar tu varita, necesitaras un Tomo de lo Arcano de tu nivel apropiado (ver +Niveles) o una mejora de varita especial. Ambos pueden ser encontrados como botin o +comprado a un mago. Para mejorar tu varita, colocala en el espacio central de la +Mesa de Trabajo Arcana y la mejora en el espacio inferior a la izquierda, y pulsar Aplicar. + +Mejoras de varita especiales mejorar un aspecto particular de la varita. Cada tipo de mejora +se puede apilar 3 veces, y el total de mejoras que puede tener una varita depende del nivel. +Las mejoras no pueden ser removidas. +PAGEBREAK +SECTION Flores y Frascos +Flores y Frascos +-------------------- +Es probable que te encuentres con flores brillantes creciendo en el suelo de tiempo +en tiempo. Esas son flores de cristal magico, una fuente de mana natural. Pueden ser +cosechadas y crafteadas para extraer mana en cristales. + +Necesitas recargar tu varita de camino? No hay problema! El mana puede ser enfrascado. +Simplemente craftea un frasco de mana con una botella de cristal y ocho cristales magicos +y llevatelo contigo. Cuando necesites mana, solo craftea la varita con el frasco para +recargar un poco. Este proceso consumira la botella. Un poco de mana se perdera en el +proceso de enfrascamiento. +PAGEBREAK +SECTION Niveles +Niveles +-------------------- +Las varitas y los hechizos vienen en cuatro niveles: BASIC_COLOURBasicoRESET_COLOUR, APPRENTICE_COLOURAprendizRESET_COLOUR, ADVANCED_COLOURAvanzadoRESET_COLOUR y MASTER_COLOURMaestroRESET_COLOUR. Cada nivel es mejor que el anterior. + +Las varitas BASIC_COLOURBasico RESET_COLOURson las que pueden ser crafteadas. Almacenan +BASIC_MAX_CHARGE mana, y solo pueden lanzar hechizos basicos. Hechizos basicos son suficientemente simples para ser lanzados por cualquiera y no usan mucho mana. Esto +no significa que son inservibles en niveles mas altos, sin embargo; su costo pequeno +hace que algunos hechizos basicos sean utiles cuando tienes acceso a hechizos maestros. + +Las varitas APPRENTICE_COLOURAprendiz RESET_COLOURson el siguiente nivel despues de las +varitas basicas. Almacenan APPRENTICE_MAX_CHARGE mana y pueden lanzar hechizos basicos y de +aprendiz. Los hechizos de aprendiz son mas llamativos que los basicos, pero cuestan mas. + +Las varitas ADVANCED_COLOURAvanzado RESET_COLOURson muy raras y mucho mas poderosas. +Pueden lanzar todos los hechizos excepto los maestros, y almacenar ADVANCED_MAX_CHARGE mana. +Los hechizos avanzados pueden otorgar poderes de superhumano como invisibilidad y causar +mucho dano a los enemigos. + +Las varitas MASTER_COLOURMaestro RESET_COLOURson las mas poderosas varitas que existen. Almacenan MASTER_MAX_CHARGE mana, y pueden lanzar cualquier hechizo. Los libros de hechizos +maestros son muy raros y los hechizos pueden causar una gran devastacion, no solo a los enemigos +si no al mundo en si. Usar con precaucion. +PAGEBREAK +SECTION Elementos +Elementos +-------------------- +Los hechizos pertenecen a diferentes elementos, los cuales describen la naturaleza +del hechizo y tambien tienen ventajas cuando son lanzados con ciertas varitas. + +FIRE_COLOURFuego +Quizas el elemento mas destructivo, fuego conlleva quemaduras, lava, y explosiones. Un piromano poderoso desatara el infierno sobre sus enemigos y probablemente encienda +el mundo en el proceso. La mayoria de los ataques de fuego queman a sus objetivos, +causando dano extra sobre el tiempo. Sin embargo, te en cuenta que no tendran efecto +en monstruos del Nether. + +ICE_COLOURHielo +El elemento de hielo esta formado por todo lo que es frio. Los hechizos de hielo +frencuentemente ralentizan a los enemigos o los congelan completamente, y son +efectivos contra criaturas de fuego. La magia de hielo puede ser util fuera de +combate, como para congelar un rio para poder cruzarlo a pie. + +LIGHTNING_COLOURRayo +Este elemento conlleva relampagos, tormentas y el clima. Un mago de tormenta poderoso +es una fuerza dura de vencer, y algunos son capaces de llamar rayos a voluntad. Los +hechizos de rayo con frecuencia hacen dano a varios enemigos a la vez y usualmente +persiguen a su objetivo, haciendolos efectivos para atacar a cualquier monstruo... +pero cuidado con los creepers. + +NECROMANCY_COLOURNigromancia +Nigromancia es el elemento de oscuridad, caos y lo no-muerto. Nigromantes son +misteriosos y a veces considerados malos, aunque no siempre es el casp. Los +hechizos de nigromancia son usados para invocar criaturas que pelearan por ti o +hasta controlar la voluntad de tus enemigos. + +EARTH_COLOURTierra +El elemento de tierra esta enfocado en el mundo natural: animales, plantas, el viento, y cosas asi. La magia de tierra es diversa y toma una gran variedad de formas, desde envenerar enemigos hasta desatar la furia del clima. Los hechizos de tierra son una liga de ataque, defensa y utilidad. + +SORCERY_COLOURHechiceria +La hechiceria es el elemento de la fuerza y el cambio. Los hechiceros manipulan la luz, +la gravedad y hasta la realidad para su beneficio. Los hechizos de hechiceria pueden +ser usados, entre otras cosas, para conceder poderes magicos y mover objetos. + +HEALING_COLOURCuracion +El elemento de curacion conlleva defensa y regeneracion. Los sanadores buscan +protegerse y a sus aliados lo mas posble, lo cual puede ser dificil para los +oponentes. A pesar de no considerarse como un ataque, la luz purificadora de sus +hechizos de curacion puede ser letales para los no-muertos. Los hechizos de +curacion son indispensables cuando se esta dentro de combate. + +Los limites entre los diferentes elementos no siempre son distintos, y algunos hechizos +tienen atributos de otros elementos mas que del suyo. + +Si eres suertudo, puedes que te encuentres una varita elemental. Estas varitas te +permiten lanzar hechizos del elemento correcto con mas potencia de lo usual. +PAGEBREAK +SECTION Armadura de Mago +Armadura de Mago +-------------------- +Como mago, necesitaras algo para protegerte de las criaturas que combates. +Ninguna armadura ordinal valdra. Para sacarle provecho a tus hechizos, necesitas +armadura de mago: sombrero, tunica, pantalones y botas. A diferencia de las otras, +estas no se romperan. Sin embargo, usaran energia arcana para proteger al usuario. +Esto significa que deben ser cargados con cristales magicos. Ventajosamente no +consumen mucho mana. Recargalos en la mesa o con frascos. + +Puedes obtener armadura de mago crafteandola con Seda Magica, obtenida al craftear +hilo con un cristal magico. + +Esos magos que se dedican a practicar un solo elemento algunas veces visten +piezas especiales las cuales ofrecen bonus a dicho elemento. Los sets completos +de esta armadura son muy codiciados entre magos y no son faciles de conseguir. + +Hay una legenda que habla sobre un sello arcano usado por los magos mas poderosos +para mejorar en sobre manera su armadura. +PAGEBREAK +SECTION Magos y Torres +Magos y Torres +-------------------- +No temas - no eres el unico que practica magia en este mundo. En tus viajes, te encontraras con torres altas con unos distintivos techos puntiagudos. +Estas son las residencias de varios magos. Una vida de soledad puede hacer que los magos se vuelvan grunones, pero usualmente son seres amigables que estan +dispuestos a compartir su conocimiento contigo - con un precio. Preparate para pagar con gemas y metales preciosos, y a cambio recibiras grandezas arcanas. +Si, en dado caso, un hechizo maestro es lo que buscas, deberar hablar con un especialista en dicho elemento. + +Desafortunadamente, hay algunos magos que no le dan la bienvenida a los visitantes. Estos magos son marginados y son hostiles hacia cualquiera que cruce su camino. Aproximate a ellos con cautela, y preparate para defenderte contra magia sumamente poderosa. Derrotalos, y su conocimiento sera tuyo. +PAGEBREAK +SECTION Recetas de Crafteo +Recetas de Crafteo +-------------------- +PAGEBREAK +PAGEBREAK +Recetas de Crafteo +-------------------- +PAGEBREAK +PAGEBREAK +Recetas de Crafteo +-------------------- +PAGEBREAK +PAGEBREAK +Recetas de Crafteo +-------------------- +PAGEBREAK +PAGEBREAK +Creditos +-------------------- +Electroblob's Wizardry +Version VERSION +Para Minecraft 1.10.2 + +Disenado, codificado y texturizado por Electroblob + +Gracias a Minecraft Forge y MCP, porque sin estos esto no hubiese sido posible. + +Gracias tambien a la comunidad de Minecraft que crea mods, la cual siempre tiene una respuesta para mis problemas! + +In addition, I'd like to thank the following individuals for their contributions to the mod: + +Translations: + +- Russian: VilagVil +- Spanish and Mexican Spanish: MadWrist + +Lightning ray sound effect from OhhWowProductions \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/texts/handbook_ru_RU.txt b/src/main/resources/assets/wizardry/texts/handbook_ru_RU.txt new file mode 100644 index 00000000..81d2fcb9 --- /dev/null +++ b/src/main/resources/assets/wizardry/texts/handbook_ru_RU.txt @@ -0,0 +1,222 @@ +PAGEBREAK +LINEBREAK + + + + +Книга Волшебника + + + +от Electroblob +PAGEBREAK +Введение +------------------------------ + +ПриветÑтвую ваÑ, волшебник! Эта книга объÑÑнÑет многие ÑпоÑобы ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¼Ð°Ð³Ð¸Ð¸ и ÑпоÑобы ее иÑпользованиÑ. + +ЕÑли вы когда-нибудь потерÑете Ñту книгу, вы можете получить новую, Ñоеденив книгу Ñ Ð¼Ð°Ð³Ð¸Ñ‡ÐµÑким криÑталлом. +PAGEBREAK +Содержание +------------------------------ +PAGEBREAK +SECTION Ðачало +Ðачало +------------------------------ +Чтобы начать работу Ñ Ð¼Ð°Ð³Ð¸ÐµÐ¹, вам понадобÑÑ‚ÑÑ Ð½ÐµÑколько вещей: + +- Волшебный жезл, Ñделанный из золотого Ñамородка, палки и магичеÑкого криÑталла. + +- ЧародейÑкий Ñтол, выполненный из 3 камней, 1 блока лазурита, 2 магичеÑких криÑталлов, 2 золотых Ñамородков и 1 фиолетового ковра. + +- Книга заклинаний. Они могут быть найдены в Ñундуках, выбиты как дроп, куплены у волшебников, или вы можете Ñделать заклинание Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ‡ÐºÐ°, магичеÑкую ракету, Ñ ÐºÐ½Ð¸Ð³Ð¾Ð¹ и 4 магичеÑкими криÑталлами. + +Вам также понадобитÑÑ ÐºÑƒÑ‡Ð° магичеÑких криÑталлов, чтобы Ñнабжать ваш жезл маной. +PAGEBREAK +SECTION Мана +Мана +------------------------------ +Мана - Ñто Ñ‚Ð°Ð¹Ð½Ð°Ñ ÑнергиÑ, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð´Ð°ÐµÑ‚ волшебникам их Ñилы. Она проÑвлÑетÑÑ Ð² вÑтречающихÑÑ Ð² природе криÑталлах, которые можно обнаружить в подземных породах. Жезлы могут хранить ману, и Ñта мана направлÑетÑÑ Ð² заклинаниÑ, которые вы иÑпользуете. Различные Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ ÑтоÑÑ‚ разного количеÑтва маны, в завиÑимоÑти от того, наÑколько они Ñильны и долговечны. +Ðаведите указатель мыши на жезл, чтобы увидеть, Ñколько маны хранитÑÑ Ð² нём. +LINEBREAK +IMAGE CRYSTAL + + + + + + + + + + + МагичеÑкого + Руда КриÑталл КриÑталла +PAGEBREAK +SECTION Жезлы +Жезлы +------------------------------ +Жезл - Ñто инÑтрумент Ð´Ð»Ñ Ð¼Ð°Ñтера. С его помощью вы можете иÑпользовать любое заклинание, еÑли жезл может Ñодержать его Ñилу (Ñм. Раздел «Уровни» Ð´Ð»Ñ Ð±Ð¾Ð»ÐµÐµ подробного объÑÑнениÑ). Жезлов +различные варианты, но вы почти навернÑка начнете Ñ Ð¾Ñновного волшебного жезла. + +Когда вы держите палочку, в углу Ñкрана будет отображатьÑÑ Ð½ÐµÐ±Ð¾Ð»ÑŒÑˆÐ¾Ð¹ Ñкран Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ¾Ð¼. Это указывает на выбранное в наÑтоÑщий момент заклинание вмеÑте Ñ Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸ÐµÐ¼, и, еÑли заклинание было иÑпользоно, кулдаун указывает времÑ, до которого заклинание может быть Ñнова иÑпользовано. +PAGEBREAK +SECTION Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ +Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ +------------------------------ +Жезл беÑполезен без заклинаний. Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð·Ð°Ð¿Ð¸ÑываютÑÑ Ð² двух формах: в книгах, которые ÑвлÑÑŽÑ‚ÑÑ Ð¿Ð¾ÑтоÑнными, и в Ñвитках, которые ÑвлÑÑŽÑ‚ÑÑ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ñ‹Ð¼Ð¸. + +Книги заклинаний мало нужны Ñами по Ñебе; ВмеÑто Ñтого они иÑпользуютÑÑ Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð²Ñзки заклинаниÑ, которое они Ñодержат к жезлам. Книги заклинаний можно найти по вÑему миру, и вам не придетÑÑ Ð´Ð¾Ð»Ð³Ð¾ иÑкать их. Когда вы её найдете, вы можете навеÑти курÑор мыши на книгу заклинаний, чтобы Ñразу увидеть оÑновную информацию о заклинании. Ð’Ñ‹ также можете щелкнуть правой кнопкой мыши, ÑƒÐ´ÐµÑ€Ð¶Ð¸Ð²Ð°Ñ ÐºÐ½Ð¸Ð³Ñƒ заклинаний, чтобы прочитать более подробную информацию о заклинании. + +С другой Ñтороны, Ñвитки Ñлужат Ð´Ð»Ñ Ð´Ñ€ÑƒÐ³Ð¾Ð¹ цели: щелчок правой кнопкой мыши в то Ð²Ñ€ÐµÐ¼Ñ ÐºÐ°Ðº удержание одной вызывает заклинание, ÑвÑзанное Ñ Ð½Ð¸Ð¼, уничтожает Ñвиток в Ñтом процеÑÑе. Как и книги заклинаний, Ñвитки можно найти по вÑему миру, но вы также можете Ñделать их Ñами, Ñоздав пуÑтой Ñвиток Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ лиÑта бумаги и нити. Затем вы можете привÑзать заклинание к Ñвитку Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ чародейÑкого Ñтола обычным ÑпоÑобом. Это приведет к тому, что количеÑтво магичеÑких криÑталлов будет равно мана-ÑтоимоÑти Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ (округлено до ближайшего к MANA_PER_CRYSTAL). + +Однако, чтобы ÑвÑзать заклинание Ñ Ñвитком, требуетÑÑ Ð·Ð½Ð°Ð½Ð¸Ðµ Ñтого заклинаниÑ, полученное только при его иÑпользовании, поÑтому еÑли вы найдете мощную книгу заклинаний, но не имеете жезла Ð´Ð»Ñ Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ, вы не можете проÑто Ñоздать Ñвиток и иÑпользовать. Тем не менее, еÑли вы найдете мощное заклинание и книгу заклинаний Ð´Ð»Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ и того же заклинаниÑ, вы можете Ñделать Ñтолько Ñвитков Ð´Ð»Ñ Ð½ÐµÐ³Ð¾, Ñколько хотите, и произнеÑти заклинание, даже не нуждаÑÑÑŒ в доÑтаточно мощной палочке. + +Свитки в оÑновном полезны, когда вам нужно иÑпользовать заклинание один или два раза в конкретном меÑте, но не хотите, чтобы он занимал Ñлот в вашем жезле. + +Однако Ñледует отметить, что Ñвитки в лучшем Ñлучае ÑвлÑÑŽÑ‚ÑÑ Ð½ÐµÑффективным методом иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ð¹. +PAGEBREAK +SECTION ЧародейÑкий Стол +ЧародейÑкий Стол +------------------------------ +ЧародейÑкий Стол - Ñто блок, похожий по внешнему виду на Ñтол зачарованиÑ, где вы можете зарÑжать, модернизировать и ÑвÑзывать Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ñ Ð²Ð°ÑˆÐ¸Ð¼ жезлом. ПроÑто помеÑтите его в любом меÑте и щелкните правой кнопкой мыши, и вы увидите интерфейÑ. +PAGEBREAK +IMAGE WORKBENCH +LINEBREAK + + + + + + + + + + + + + + ЧародейÑкий Стол +PAGEBREAK +SECTION СвÑзывание заклинаний +СвÑзывание заклинаний +------------------------------ +Чтобы привÑзать заклинание к Ñвоему жезлу, проÑто помеÑтите книгу заклинаний в один из пÑти Ñлотов вокруг ÐºÑ€Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‡ÐµÐ³Ð¾ Ñтола и жезла в центральном Ñлоте и нажмите кнопку ПринÑть. Одновременно можно ÑвÑзать до пÑти заклинаний Ñ Ð¶ÐµÐ·Ð»Ð¾Ð¼. Ð£Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°Ñ Ð¶ÐµÐ·Ð», вы можете переключатьÑÑ Ð¼ÐµÐ¶Ð´Ñƒ заклинаниÑми Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ клавиш NEXT_SPELL_KEY и PREVIOUS_SPELL_KEY (их можно изменить в наÑтройках -> управлениÑ). + +SECTION ЗарÑдка жезлов +ЗарÑди Ñвой жезл +------------------------------ +Чтобы зарÑдить Ñвой жезл, помеÑтите жезл в центральный Ñлот чародейÑкого верÑтака и помеÑтите некоторые магичеÑкие криÑталлы в Ñлот влево, а затем нажмите Применить. Каждый криÑталл Ñтоит MANA_PER_CRYSTAL маны, и жезл будет принимать только то, что ему нужно, поÑтому вы можете Ñохранить Ð·Ð°Ð¿Ð°Ñ ÐºÑ€Ð¸Ñталлов в рабочем меÑте, когда вам Ñто нужно. Однако, имейте в виду, что жезл может взÑть только целые криÑталлы, поÑтому, еÑли жезлу понадобитÑÑ, например, еще 30 маны, MANA_PER_CRYSTAL_MINUS_30 маны будет потерÑно при зарÑдке. Ð’Ñе жезлы будут брать точное количеÑтво криÑталлов Ð´Ð»Ñ Ð·Ð°Ñ€Ñдки, еÑли они пуÑты, еÑли только они не применены к улучшению хранилища. + +SECTION Улучшение Жезла +Улучши Свой Жезл +------------------------------ +Чтобы обновить жезл, вам понадобитÑÑ Ñ„Ð¾Ð»Ð¸Ð°Ð½Ñ‚ арканы ÑоответÑтвующего ÑƒÑ€Ð¾Ð²Ð½Ñ (Ñм. Раздел «Уровни») или Ñпециальное улучшение жезла. Оба они могут быть найдены как добыча или приобретены у волшебника. Чтобы обновить жезл, помеÑтите его в центральный Ñлот и помеÑтите улучшение, затем нажмите «Применить». + +Специальные ÑƒÐ»ÑƒÑ‡ÑˆÐµÐ½Ð¸Ñ Ð¶ÐµÐ·Ð»Ð° улучшают конкретный аÑпект жезла. Каждый тип ÑƒÐ»ÑƒÑ‡ÑˆÐµÐ½Ð¸Ñ Ð¼Ð¾Ð¶ÐµÑ‚ ÑкладыватьÑÑ Ð´Ð¾ трех раз, и общее количеÑтво улучшений, которые может взÑть жезл, завиÑит от его уровнÑ. Ðе удаетÑÑ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ улучшениÑ. +PAGEBREAK +SECTION Цветы И Бутылки +Цветы И Бутылки +------------------------------ +Ð’Ñ‹, вероÑтно, ÑтолкнетеÑÑŒ Ñ Ð»ÑŽÐ±Ð¾Ð¿Ñ‹Ñ‚Ð½Ñ‹Ð¼Ð¸ ÑветÑщимиÑÑ Ñ†Ð²ÐµÑ‚Ð°Ð¼Ð¸, которые Ð²Ñ€ÐµÐ¼Ñ Ð¾Ñ‚ времени раÑтут в дикой природе. Это хруÑтальные цветы, еÑтеÑтвенный иÑточник маны. Они могут быть Ñобраны и обработаны Ð´Ð»Ñ Ð¸Ð·Ð²Ð»ÐµÑ‡ÐµÐ½Ð¸Ñ Ð¼Ð°Ð½Ñ‹ в виде криÑталлов. + +Вам нужно зарÑдить жезл на ходу? Ðет проблем! Теперь можно разливать ману. ПроÑто Ñоздайте колбу маны Ñо ÑтеклÑнной бутылкой и воÑемью магичеÑкими криÑталлами и возьмите ее Ñ Ñобой. Когда вам нужно иÑпользовать его, проÑто Ñоедените его Ñвоим жезлом, и он воÑÑтановит чаÑть зарÑда. Этот процеÑÑ Ð±ÑƒÐ´ÐµÑ‚ потреблÑть бутылку. ÐÐµÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¼Ð°Ð½Ð° терÑетÑÑ Ð²Ð¾ Ð²Ñ€ÐµÐ¼Ñ Ñ€Ð¾Ð·Ð»Ð¸Ð²Ð°. +PAGEBREAK +SECTION Уровни +Уровни +------------------------------ +Жезлы и Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð²Ñ…Ð¾Ð´ÑÑ‚ в четыре уровнÑ: BASIC_COLOURначинающийRESET_COLOUR, APPRENTICE_COLOURученикRESET_COLOUR, ADVANCED_COLOURпродвинутый RESET_COLOURи MASTER_COLOURмаÑтерRESET_COLOUR. Каждый уровень более мощный, чем поÑледний. + +BASIC_COLOURначинающий RESET_COLOURжезлы-которые могут быть Ñозданы. Они выдерживают BASIC_MAX_CHARGE мана, и может иÑпользовать только Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð´Ð»Ñ Ð½Ð°Ñ‡Ð¸Ð½Ð°ÑŽÑ‰Ð¸Ñ…. Ðачинающие Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð´Ð¾Ñтаточно проÑты, чтобы их броÑали вÑе, и они обычно не ÑтоÑÑ‚ много маны. Это не обÑзательно означает, что они беÑполезны на более выÑоких уровнÑÑ…; Их Ð½Ð¸Ð·ÐºÐ°Ñ ÑтоимоÑть делает некоторые Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð½Ð°Ñ‡Ð¸Ð½Ð°ÑŽÑ‰Ð¸Ñ… полезными даже тогда, когда у Ð²Ð°Ñ ÐµÑть доÑтуп к маÑтер-заклинаниÑм.. + +APPRENTICE_COLOURУченик RESET_COLOURжезлы Ñледующие поÑле начинающих. Они выдерживают APPRENTICE_MAX_CHARGE ману и может применÑть Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð´Ð»Ñ Ð½Ð°Ñ‡Ð¸Ð½Ð°ÑŽÑ‰Ð¸Ñ… и учеников. Ученики заклинаний немного более впечатлÑющие, чем Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð½Ð°Ñ‡Ð¸Ð½Ð°ÑŽÑ‰Ð¸Ñ…, но обычно ÑтоÑÑ‚ дороже. + +ADVANCED_COLOURПродвинутый RESET_COLOURжезлы довольно редки и гораздо мощнее. Они могут иÑпользовать вÑе заклинаниÑ, кроме маÑтер-заклинаний, и удерживать ADVANCED_MAX_CHARGE ману. РаÑширенные Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ предоÑтавлÑть ÑверхчеловечеÑкие Ñилы, такие как невидимоÑть и наноÑить урон врагам. + +MASTER_COLOURМаÑтер RESET_COLOURжезлы Ñамые могучие в мире. Они выдерживают MASTER_MAX_CHARGE ману, И может накладывать любое заклинание. МагиÑтры заклинаний очень редки, и Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ нанеÑти полный урон не только врагам, но даже Ñамому миру. ИÑпользуйте Ñ Ð¾ÑторожноÑтью. +PAGEBREAK +SECTION Стихии +Стихии +------------------------------ +Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð¿Ñ€Ð¸Ð½Ð°Ð´Ð»ÐµÐ¶Ð°Ñ‚ к различным Ñлементам, которые опиÑывают природу Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð¸ также имеют перки при иÑпользовании Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð½Ñ‹Ð¼Ð¸ жезлами. + +FIRE_COLOURОгонь +Пожалуй, Ñамый разрушительный Ñлемент, огонь каÑаетÑÑ Ð³Ð¾Ñ€ÐµÐ½Ð¸Ñ, лавы и взрывов. Мощный пиромант развÑжет ад Ñвоим врагам и, возможно, запуÑтит мир в Ñтом процеÑÑе. БольшинÑтво заклинаний Ñ Ð¾Ð³Ð½ÐµÐ½Ð½Ð¾Ð¹ атакой поджигает цель, наноÑÑ ÐµÐ¹ поÑтоÑнный урон Ñ Ñ‚ÐµÑ‡ÐµÐ½Ð¸ÐµÐ¼ времени. Однако будьте оÑторожны, что Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð¾Ð³Ð½Ñ Ð¼Ð¾Ð³ÑƒÑ‚ не влиÑть на нижних мобов. + +ICE_COLOURЛёд +Элемент льда Ñделан из вÑего, что холодно. Морозные Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ñ‡Ð°Ñто замедлÑÑŽÑ‚ врагов или замораживают их полноÑтью, и оÑобенно Ñффективны против ÑущеÑтв огнÑ. ÐœÐ°Ð³Ð¸Ñ Ð¼Ð°Ð³Ð¸Ð¸ льда может оказатьÑÑ Ñтоль же полезной и вне боÑ, как замораживание воды Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÑÐµÑ‡ÐµÐ½Ð¸Ñ Ñ€ÐµÐºÐ¸. + +LIGHTNING_COLOURÐœÐ¾Ð»Ð½Ð¸Ñ +Этот Ñлемент каÑаетÑÑ Ð¼Ð¾Ð»Ð½Ð¸Ð¸, штормов и погоды. Мощный штормовой маг - Ñто Ñила, Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð¹ нужно ÑчитатьÑÑ, а некоторые обладают ÑпоÑобноÑтью призывать молнию по желанию. МолниеноÑные Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ñ‡Ð°Ñто наноÑÑÑ‚ урон множеÑтву врагов Ñразу и обычно ищут Ñвою цель, Ð´ÐµÐ»Ð°Ñ Ð¸Ñ… Ñффективной атакой против любых мобов ... но оÑтерегайтеÑÑŒ криперов. + + +NECROMANCY_COLOURÐÐµÐºÑ€Ð¾Ð¼Ð°Ð½Ñ‚Ð¸Ñ +ÐÐµÐºÑ€Ð¾Ð¼Ð°Ð½Ñ‚Ð¸Ñ - Ñто Ñлемент тьмы, хаоÑа и нежити. Ðекроманты таинÑтвенны и чаÑто раÑÑматриваютÑÑ ÐºÐ°Ðº злые, Ñ…Ð¾Ñ‚Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ð¾ Ñто не так. Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð½ÐµÐºÑ€Ð¾Ð¼Ð°Ð½Ñ‚Ð¸Ð¸ обычно иÑпользуютÑÑ, чтобы призвать ÑущеÑтв, чтобы ÑражатьÑÑ Ð·Ð° Ð²Ð°Ñ Ð¸Ð»Ð¸ даже Ñгибать волю ваших врагов. + +EARTH_COLOURÐ—ÐµÐ¼Ð»Ñ +Элемент земли ÑфокуÑирован на природном мире: животных, раÑтениÑÑ…, ветре и Ñ‚. п. ÐœÐ°Ð³Ð¸Ñ Ð—ÐµÐ¼Ð»Ð¸ разнообразна и принимает Ñамые различные формы: от Ð¾Ñ‚Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð²Ñ€Ð°Ð³Ð¾Ð² до развÑÐ·Ñ‹Ð²Ð°Ð½Ð¸Ñ ÑроÑти погоды. Земные Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð¿Ñ€ÐµÐ´ÑтавлÑÑŽÑ‚ Ñобой ÑмеÑÑŒ атак, защиты и полезноÑти. + +SORCERY_COLOURКолдовÑтво +ВолшебÑтво - Ñто Ñлемент Ñилы и изменениÑ. Волшебники манипулируют Ñветом, гравитацией и даже Ñамой реальноÑтью, чтобы ÑоответÑтвовать их потребноÑÑ‚Ñм. КолдовÑкие Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ иÑпользоватьÑÑ, помимо вÑего прочего, Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´Ð¾ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¼Ð°Ð³Ð¸Ñ‡ÐµÑким Ñилам Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð»Ð¸ Ð¿ÐµÑ€ÐµÐ¼ÐµÑ‰ÐµÐ½Ð¸Ñ Ð¿Ñ€ÐµÐ´Ð¼ÐµÑ‚Ð¾Ð² по их желанию. + +HEALING_COLOURИÑцеление +Элемент иÑÑ†ÐµÐ»ÐµÐ½Ð¸Ñ ÑвÑзан Ñ Ð·Ð°Ñ‰Ð¸Ñ‚Ð¾Ð¹ и регенерацией. Целители ÑтараютÑÑ ÐºÐ°Ðº можно больше защитить ÑÐµÐ±Ñ Ð¸ Ñвоих Ñоюзников и как таковые могут быть очень трудными противниками. Ð¥Ð¾Ñ‚Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ð¾ не иÑпользуетÑÑ Ð² качеÑтве атаки, очищающий Ñвет некоторых иÑцелÑющих заклинаний нанеÑет значительный урон нежити. ИÑцелÑющие Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð½ÐµÐ·Ð°Ð¼ÐµÐ½Ð¸Ð¼Ñ‹ в бою. + +Границы между различными Ñлементами не вÑегда различны, и некоторые Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð½ÐµÑут в Ñебе черты других Ñлементов, отличных от их ÑобÑтвенных. + +ЕÑли вам повезет, вы можете напаÑть на Ñлементарный жезл. Эти жезлы позволÑÑ‚ вам иÑпользовать Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¾Ð³Ð¾ Ñлемента Ð´Ð»Ñ Ð±Ð¾Ð»ÑŒÑˆÐµÐ¹ Ñилы, чем обычно. +PAGEBREAK +SECTION ОдеÑние Волшебника +ОдеÑние Волшебника +------------------------------ +Как волшебник, вам понадобитÑÑ Ñ‡Ñ‚Ð¾-то, чтобы защитить Ð²Ð°Ñ Ð¾Ñ‚ ÑущеÑтв, Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ð¼Ð¸ вы ÑражаетеÑÑŒ. Однако обычные доÑпехи не Ñделают Ñтого. Чтобы макÑимально иÑпользовать ваши заклинаниÑ, вам понадобÑÑ‚ÑÑ Ð¼Ð°Ð³Ð¸Ñ‡ÐµÑкие доÑпехи: шлÑпа, халаты, лоÑины и Ñапоги. Ð’ отличие от обычной брони, они не ÑломаютÑÑ. ВмеÑто Ñтого они иÑпользуют тайную Ñнергию Ð´Ð»Ñ Ð·Ð°Ñ‰Ð¸Ñ‚Ñ‹ ноÑителÑ. Это, конечно, означает, что они должны быть зарÑжены магичеÑкими криÑталлами, как и жезлы. К ÑчаÑтью, Ñти предметы одежды не потреблÑÑŽÑ‚ много маны. ЗарÑдите их, как жезл в чародейÑком Ñтоле или в колбе маны, но будьте оÑторожны - еÑли у них закончитÑÑ Ð¼Ð°Ð½Ð°, вы окажетеÑÑŒ беззащитными. + +Ð’Ñ‹ можете получить волшебную броню, ÑÐ¾Ð·Ð´Ð°Ð²Ð°Ñ ÐµÐµ из магичеÑкого шелка, получаемого при Ñоздании нитки Ñ Ð¼Ð°Ð³Ð¸Ñ‡ÐµÑким криÑталлом. + +Те волшебники, которые поÑвÑщают ÑÐµÐ±Ñ Ð¿Ñ€Ð°ÐºÑ‚Ð¸ÐºÐµ одного Ñлемента, иногда ноÑÑÑ‚ Ñпециальные одежды, которые дают бонуÑÑ‹ за Ñтот Ñлемент. Полные комплекты такой одежды очень популÑрны Ñреди маÑтеров, и их не так проÑто найти. + +Легенда говорит о тайной печати, иÑпользуемой Ñамыми могущеÑтвенными волшебниками, чтобы значительно улучшить их броню. +PAGEBREAK +SECTION Волшебники И Башни +Волшебники И Башни +------------------------------ +Ðе бойтеÑÑŒ - вы не единÑтвенный практикующий в Ñтом мире. Ð’ ваших путешеÑтвиÑÑ… вы можете вÑтретить выÑокие башни Ñ Ñ…Ð°Ñ€Ð°ÐºÑ‚ÐµÑ€Ð½Ñ‹Ð¼Ð¸ оÑтроконечными крышами. Это резиденции волшебников. Жизнь одиночеÑтва может иногда приводить в замешательÑтво магов, но они, как правило, дружелюбные люди, которые готовы поделитьÑÑ Ñ Ð²Ð°Ð¼Ð¸ Ñвоими знаниÑми, Ñ…Ð¾Ñ‚Ñ Ð¸ за определенную плату. Ожидайте оплатить драгоценные металлы и драгоценные камни, и взамен вы получите много чудеÑных загадок. ЕÑли, однако, Ñто магичеÑкое заклинание, которое вы ищете, вам нужно поговорить Ñо ÑпециалиÑтом. +PAGEBREAK +PAGEBREAK +SECTION Рецепты +Рецепты +------------------------------ +PAGEBREAK +PAGEBREAK +Рецепты +------------------------------ +PAGEBREAK +PAGEBREAK +Рецепты +------------------------------ +PAGEBREAK +PAGEBREAK +Рецепты +------------------------------ +PAGEBREAK +PAGEBREAK +Credits +------------------------------ +Electroblob's Wizardry +Version VERSION +For Minecraft 1.10.2 + +Designed, coded and textured by Electroblob + +Thanks to Minecraft Forge and MCP, without which this mod would not have been possible. + +Thanks also to the Minecraft modding community, which always has an answer to my modding problems! + +In addition, I'd like to thank the following individuals for their contributions to the mod: + +Translations: + +- Russian: VilagVil +- Spanish and Mexican Spanish: MadWrist + +Lightning ray sound effect from OhhWowProductions \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/textures/armour/invisible_armour.png b/src/main/resources/assets/wizardry/textures/armour/invisible_armour.png new file mode 100644 index 00000000..892b9537 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/invisible_armour.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/spectral_armour.png b/src/main/resources/assets/wizardry/textures/armour/spectral_armour.png new file mode 100644 index 00000000..748577d3 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/spectral_armour.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/spectral_armour_legs.png b/src/main/resources/assets/wizardry/textures/armour/spectral_armour_legs.png new file mode 100644 index 00000000..584f1e9c Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/spectral_armour_legs.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour.png new file mode 100644 index 00000000..4f391e29 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_earth.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_earth.png new file mode 100644 index 00000000..46ec5132 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_earth.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_earth_legs.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_earth_legs.png new file mode 100644 index 00000000..d6ef5a39 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_earth_legs.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_fire.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_fire.png new file mode 100644 index 00000000..5040c866 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_fire.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_fire_legs.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_fire_legs.png new file mode 100644 index 00000000..369e2842 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_fire_legs.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_healing.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_healing.png new file mode 100644 index 00000000..866fd486 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_healing.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_healing_legs.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_healing_legs.png new file mode 100644 index 00000000..6f68d17b Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_healing_legs.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_ice.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_ice.png new file mode 100644 index 00000000..3e25a69c Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_ice.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_ice_legs.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_ice_legs.png new file mode 100644 index 00000000..a62eef4e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_ice_legs.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_legs.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_legs.png new file mode 100644 index 00000000..5cb7de5b Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_legs.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_lightning.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_lightning.png new file mode 100644 index 00000000..29cf5755 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_lightning.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_lightning_legs.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_lightning_legs.png new file mode 100644 index 00000000..2232d817 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_lightning_legs.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_necromancy.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_necromancy.png new file mode 100644 index 00000000..1efbdf7b Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_necromancy.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_necromancy_legs.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_necromancy_legs.png new file mode 100644 index 00000000..e6624704 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_necromancy_legs.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_sorcery.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_sorcery.png new file mode 100644 index 00000000..d5e31f09 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_sorcery.png differ diff --git a/src/main/resources/assets/wizardry/textures/armour/wizard_armour_sorcery_legs.png b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_sorcery_legs.png new file mode 100644 index 00000000..2b381f5c Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/armour/wizard_armour_sorcery_legs.png differ diff --git a/src/main/resources/assets/wizardry/textures/blocks/Thumbs.db b/src/main/resources/assets/wizardry/textures/blocks/Thumbs.db new file mode 100644 index 00000000..412f690c Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/blocks/Thumbs.db differ diff --git a/src/main/resources/assets/wizardry/textures/blocks/arcane_workbench_bottom.png b/src/main/resources/assets/wizardry/textures/blocks/arcane_workbench_bottom.png new file mode 100644 index 00000000..0271489e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/blocks/arcane_workbench_bottom.png differ diff --git a/src/main/resources/assets/wizardry/textures/blocks/arcane_workbench_side.png b/src/main/resources/assets/wizardry/textures/blocks/arcane_workbench_side.png new file mode 100644 index 00000000..49ddaf0a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/blocks/arcane_workbench_side.png differ diff --git a/src/main/resources/assets/wizardry/textures/blocks/arcane_workbench_top.png b/src/main/resources/assets/wizardry/textures/blocks/arcane_workbench_top.png new file mode 100644 index 00000000..2b772b89 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/blocks/arcane_workbench_top.png differ diff --git a/src/main/resources/assets/wizardry/textures/blocks/crystal_block.png b/src/main/resources/assets/wizardry/textures/blocks/crystal_block.png new file mode 100644 index 00000000..1a8ece1c Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/blocks/crystal_block.png differ diff --git a/src/main/resources/assets/wizardry/textures/blocks/crystal_flower.png b/src/main/resources/assets/wizardry/textures/blocks/crystal_flower.png new file mode 100644 index 00000000..65b5ec0e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/blocks/crystal_flower.png differ diff --git a/src/main/resources/assets/wizardry/textures/blocks/crystal_ore.png b/src/main/resources/assets/wizardry/textures/blocks/crystal_ore.png new file mode 100644 index 00000000..f9e633b2 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/blocks/crystal_ore.png differ diff --git a/src/main/resources/assets/wizardry/textures/blocks/meteor.png b/src/main/resources/assets/wizardry/textures/blocks/meteor.png new file mode 100644 index 00000000..53b1f199 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/blocks/meteor.png differ diff --git a/src/main/resources/assets/wizardry/textures/blocks/snare.png b/src/main/resources/assets/wizardry/textures/blocks/snare.png new file mode 100644 index 00000000..2acac804 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/blocks/snare.png differ diff --git a/src/main/resources/assets/wizardry/textures/blocks/spectral_block.png b/src/main/resources/assets/wizardry/textures/blocks/spectral_block.png new file mode 100644 index 00000000..e9abbcfa Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/blocks/spectral_block.png differ diff --git a/src/main/resources/assets/wizardry/textures/blocks/transportation_stone.png b/src/main/resources/assets/wizardry/textures/blocks/transportation_stone.png new file mode 100644 index 00000000..f3a1aa75 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/blocks/transportation_stone.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/Thumbs.db b/src/main/resources/assets/wizardry/textures/entity/Thumbs.db new file mode 100644 index 00000000..40dce791 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/Thumbs.db differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_0.png b/src/main/resources/assets/wizardry/textures/entity/arc_0.png new file mode 100644 index 00000000..9d0c4a46 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_0.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_1.png b/src/main/resources/assets/wizardry/textures/entity/arc_1.png new file mode 100644 index 00000000..35470422 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_1.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_10.png b/src/main/resources/assets/wizardry/textures/entity/arc_10.png new file mode 100644 index 00000000..82ec1d74 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_10.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_11.png b/src/main/resources/assets/wizardry/textures/entity/arc_11.png new file mode 100644 index 00000000..f57e4bad Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_11.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_12.png b/src/main/resources/assets/wizardry/textures/entity/arc_12.png new file mode 100644 index 00000000..43c78d93 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_12.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_13.png b/src/main/resources/assets/wizardry/textures/entity/arc_13.png new file mode 100644 index 00000000..b8372d7d Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_13.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_14.png b/src/main/resources/assets/wizardry/textures/entity/arc_14.png new file mode 100644 index 00000000..9e17a144 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_14.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_15.png b/src/main/resources/assets/wizardry/textures/entity/arc_15.png new file mode 100644 index 00000000..d3fd0a96 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_15.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_2.png b/src/main/resources/assets/wizardry/textures/entity/arc_2.png new file mode 100644 index 00000000..a281efed Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_2.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_3.png b/src/main/resources/assets/wizardry/textures/entity/arc_3.png new file mode 100644 index 00000000..cdca5f2d Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_3.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_4.png b/src/main/resources/assets/wizardry/textures/entity/arc_4.png new file mode 100644 index 00000000..1085eb86 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_4.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_5.png b/src/main/resources/assets/wizardry/textures/entity/arc_5.png new file mode 100644 index 00000000..486881b1 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_5.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_6.png b/src/main/resources/assets/wizardry/textures/entity/arc_6.png new file mode 100644 index 00000000..4038e71e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_6.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_7.png b/src/main/resources/assets/wizardry/textures/entity/arc_7.png new file mode 100644 index 00000000..383ef639 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_7.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_8.png b/src/main/resources/assets/wizardry/textures/entity/arc_8.png new file mode 100644 index 00000000..0dc3a0e3 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_8.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/arc_9.png b/src/main/resources/assets/wizardry/textures/entity/arc_9.png new file mode 100644 index 00000000..4ffae58f Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/arc_9.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/aura.png b/src/main/resources/assets/wizardry/textures/entity/aura.png new file mode 100644 index 00000000..89e2234b Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/aura.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/black_hole.png b/src/main/resources/assets/wizardry/textures/entity/black_hole.png new file mode 100644 index 00000000..4ef20003 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/black_hole.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/dark_orb.png b/src/main/resources/assets/wizardry/textures/entity/dark_orb.png new file mode 100644 index 00000000..0de0ee6a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/dark_orb.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/dark_ray.png b/src/main/resources/assets/wizardry/textures/entity/dark_ray.png new file mode 100644 index 00000000..e27bca23 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/dark_ray.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/darkness_orb.png b/src/main/resources/assets/wizardry/textures/entity/darkness_orb.png new file mode 100644 index 00000000..c43ff6d7 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/darkness_orb.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/dart.png b/src/main/resources/assets/wizardry/textures/entity/dart.png new file mode 100644 index 00000000..ff59654a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/dart.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/decay_0.png b/src/main/resources/assets/wizardry/textures/entity/decay_0.png new file mode 100644 index 00000000..f2f94a28 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/decay_0.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/decay_1.png b/src/main/resources/assets/wizardry/textures/entity/decay_1.png new file mode 100644 index 00000000..cabe4eb0 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/decay_1.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/decay_2.png b/src/main/resources/assets/wizardry/textures/entity/decay_2.png new file mode 100644 index 00000000..213dc826 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/decay_2.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/decay_3.png b/src/main/resources/assets/wizardry/textures/entity/decay_3.png new file mode 100644 index 00000000..7fe8ee41 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/decay_3.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/decay_4.png b/src/main/resources/assets/wizardry/textures/entity/decay_4.png new file mode 100644 index 00000000..639ce2ad Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/decay_4.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/decay_5.png b/src/main/resources/assets/wizardry/textures/entity/decay_5.png new file mode 100644 index 00000000..92384183 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/decay_5.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/decay_6.png b/src/main/resources/assets/wizardry/textures/entity/decay_6.png new file mode 100644 index 00000000..5adb70a2 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/decay_6.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/decay_7.png b/src/main/resources/assets/wizardry/textures/entity/decay_7.png new file mode 100644 index 00000000..52b7d6dc Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/decay_7.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/decay_8.png b/src/main/resources/assets/wizardry/textures/entity/decay_8.png new file mode 100644 index 00000000..98951c88 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/decay_8.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/decay_9.png b/src/main/resources/assets/wizardry/textures/entity/decay_9.png new file mode 100644 index 00000000..35b41070 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/decay_9.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/evil_wizard_0.png b/src/main/resources/assets/wizardry/textures/entity/evil_wizard_0.png new file mode 100644 index 00000000..a5dbce21 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/evil_wizard_0.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/evil_wizard_1.png b/src/main/resources/assets/wizardry/textures/entity/evil_wizard_1.png new file mode 100644 index 00000000..23f91935 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/evil_wizard_1.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/evil_wizard_2.png b/src/main/resources/assets/wizardry/textures/entity/evil_wizard_2.png new file mode 100644 index 00000000..c98d7903 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/evil_wizard_2.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/evil_wizard_3.png b/src/main/resources/assets/wizardry/textures/entity/evil_wizard_3.png new file mode 100644 index 00000000..8cb1fb91 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/evil_wizard_3.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/evil_wizard_4.png b/src/main/resources/assets/wizardry/textures/entity/evil_wizard_4.png new file mode 100644 index 00000000..439e6320 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/evil_wizard_4.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/evil_wizard_5.png b/src/main/resources/assets/wizardry/textures/entity/evil_wizard_5.png new file mode 100644 index 00000000..18f4223e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/evil_wizard_5.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/fire_sigil.png b/src/main/resources/assets/wizardry/textures/entity/fire_sigil.png new file mode 100644 index 00000000..d899e253 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/fire_sigil.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/firebolt.png b/src/main/resources/assets/wizardry/textures/entity/firebolt.png new file mode 100644 index 00000000..31316107 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/firebolt.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/force_arrow.png b/src/main/resources/assets/wizardry/textures/entity/force_arrow.png new file mode 100644 index 00000000..ffb628a1 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/force_arrow.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/force_orb.png b/src/main/resources/assets/wizardry/textures/entity/force_orb.png new file mode 100644 index 00000000..8198db00 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/force_orb.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/frost_sigil.png b/src/main/resources/assets/wizardry/textures/entity/frost_sigil.png new file mode 100644 index 00000000..9718612c Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/frost_sigil.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/healing_aura.png b/src/main/resources/assets/wizardry/textures/entity/healing_aura.png new file mode 100644 index 00000000..db683f9f Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/healing_aura.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/ice_charge.png b/src/main/resources/assets/wizardry/textures/entity/ice_charge.png new file mode 100644 index 00000000..0c44b047 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/ice_charge.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/ice_giant.png b/src/main/resources/assets/wizardry/textures/entity/ice_giant.png new file mode 100644 index 00000000..bd6ef77a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/ice_giant.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/ice_lance.png b/src/main/resources/assets/wizardry/textures/entity/ice_lance.png new file mode 100644 index 00000000..7013c060 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/ice_lance.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/ice_shard.png b/src/main/resources/assets/wizardry/textures/entity/ice_shard.png new file mode 100644 index 00000000..81203ba6 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/ice_shard.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/ice_spike.png b/src/main/resources/assets/wizardry/textures/entity/ice_spike.png new file mode 100644 index 00000000..24a7e5e3 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/ice_spike.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/ice_wraith.png b/src/main/resources/assets/wizardry/textures/entity/ice_wraith.png new file mode 100644 index 00000000..0aa87596 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/ice_wraith.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/light_aura.png b/src/main/resources/assets/wizardry/textures/entity/light_aura.png new file mode 100644 index 00000000..286254e0 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/light_aura.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/light_ray.png b/src/main/resources/assets/wizardry/textures/entity/light_ray.png new file mode 100644 index 00000000..79d11862 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/light_ray.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/lightning_arrow.png b/src/main/resources/assets/wizardry/textures/entity/lightning_arrow.png new file mode 100644 index 00000000..74e34f7e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/lightning_arrow.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/lightning_hammer.png b/src/main/resources/assets/wizardry/textures/entity/lightning_hammer.png new file mode 100644 index 00000000..740c0724 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/lightning_hammer.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_0.png b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_0.png new file mode 100644 index 00000000..eec7d4df Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_0.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_1.png b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_1.png new file mode 100644 index 00000000..b39de9ba Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_1.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_2.png b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_2.png new file mode 100644 index 00000000..a2e821e0 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_2.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_3.png b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_3.png new file mode 100644 index 00000000..2e62c2be Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_3.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_4.png b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_4.png new file mode 100644 index 00000000..8dd2d011 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_4.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_5.png b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_5.png new file mode 100644 index 00000000..2ba2a220 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_5.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_6.png b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_6.png new file mode 100644 index 00000000..460091e7 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_6.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_7.png b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_7.png new file mode 100644 index 00000000..0a1ca101 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/lightning_pulse_7.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/lightning_sigil.png b/src/main/resources/assets/wizardry/textures/entity/lightning_sigil.png new file mode 100644 index 00000000..2ce06d2b Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/lightning_sigil.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/lightning_wraith.png b/src/main/resources/assets/wizardry/textures/entity/lightning_wraith.png new file mode 100644 index 00000000..0328b425 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/lightning_wraith.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/magic_missile.png b/src/main/resources/assets/wizardry/textures/entity/magic_missile.png new file mode 100644 index 00000000..ead49d45 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/magic_missile.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/phoenix.png b/src/main/resources/assets/wizardry/textures/entity/phoenix.png new file mode 100644 index 00000000..3451a162 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/phoenix.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/pointer.png b/src/main/resources/assets/wizardry/textures/entity/pointer.png new file mode 100644 index 00000000..281feff4 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/pointer.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/ring_of_fire.png b/src/main/resources/assets/wizardry/textures/entity/ring_of_fire.png new file mode 100644 index 00000000..1f5d8e8f Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/ring_of_fire.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/rune.png b/src/main/resources/assets/wizardry/textures/entity/rune.png new file mode 100644 index 00000000..2d16b3e0 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/rune.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/shadow_ward.png b/src/main/resources/assets/wizardry/textures/entity/shadow_ward.png new file mode 100644 index 00000000..956391bd Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/shadow_ward.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/shield.png b/src/main/resources/assets/wizardry/textures/entity/shield.png new file mode 100644 index 00000000..db964c49 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/shield.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/sixth_sense.png b/src/main/resources/assets/wizardry/textures/entity/sixth_sense.png new file mode 100644 index 00000000..5187f19e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/sixth_sense.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/spark.png b/src/main/resources/assets/wizardry/textures/entity/spark.png new file mode 100644 index 00000000..130ac30c Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/spark.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/spark_bomb.png b/src/main/resources/assets/wizardry/textures/entity/spark_bomb.png new file mode 100644 index 00000000..7d7adbe3 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/spark_bomb.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/spirit_horse.png b/src/main/resources/assets/wizardry/textures/entity/spirit_horse.png new file mode 100644 index 00000000..c633c008 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/spirit_horse.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/spirit_wolf.png b/src/main/resources/assets/wizardry/textures/entity/spirit_wolf.png new file mode 100644 index 00000000..e22cff8f Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/spirit_wolf.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/stone_statue_32.png b/src/main/resources/assets/wizardry/textures/entity/stone_statue_32.png new file mode 100644 index 00000000..c624f268 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/stone_statue_32.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/stone_statue_64.png b/src/main/resources/assets/wizardry/textures/entity/stone_statue_64.png new file mode 100644 index 00000000..2a7a2ad9 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/stone_statue_64.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/target_pointer.png b/src/main/resources/assets/wizardry/textures/entity/target_pointer.png new file mode 100644 index 00000000..fd0a2aba Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/target_pointer.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/wing.png b/src/main/resources/assets/wizardry/textures/entity/wing.png new file mode 100644 index 00000000..da24e164 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/wing.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/wizard_0.png b/src/main/resources/assets/wizardry/textures/entity/wizard_0.png new file mode 100644 index 00000000..e613565a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/wizard_0.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/wizard_1.png b/src/main/resources/assets/wizardry/textures/entity/wizard_1.png new file mode 100644 index 00000000..7e80128a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/wizard_1.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/wizard_2.png b/src/main/resources/assets/wizardry/textures/entity/wizard_2.png new file mode 100644 index 00000000..f008cdd3 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/wizard_2.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/wizard_3.png b/src/main/resources/assets/wizardry/textures/entity/wizard_3.png new file mode 100644 index 00000000..1193ade2 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/wizard_3.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/wizard_4.png b/src/main/resources/assets/wizardry/textures/entity/wizard_4.png new file mode 100644 index 00000000..2b678753 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/wizard_4.png differ diff --git a/src/main/resources/assets/wizardry/textures/entity/wizard_5.png b/src/main/resources/assets/wizardry/textures/entity/wizard_5.png new file mode 100644 index 00000000..d8263f1a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/entity/wizard_5.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/Thumbs.db b/src/main/resources/assets/wizardry/textures/gui/Thumbs.db new file mode 100644 index 00000000..0eb497c0 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/Thumbs.db differ diff --git a/src/main/resources/assets/wizardry/textures/gui/arcane_workbench.png b/src/main/resources/assets/wizardry/textures/gui/arcane_workbench.png new file mode 100644 index 00000000..02a43a70 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/arcane_workbench.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/decay_icon.png b/src/main/resources/assets/wizardry/textures/gui/decay_icon.png new file mode 100644 index 00000000..d61ae029 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/decay_icon.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/element_icon_earth.png b/src/main/resources/assets/wizardry/textures/gui/element_icon_earth.png new file mode 100644 index 00000000..9413854f Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/element_icon_earth.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/element_icon_fire.png b/src/main/resources/assets/wizardry/textures/gui/element_icon_fire.png new file mode 100644 index 00000000..37cb27c6 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/element_icon_fire.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/element_icon_healing.png b/src/main/resources/assets/wizardry/textures/gui/element_icon_healing.png new file mode 100644 index 00000000..4225f1d2 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/element_icon_healing.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/element_icon_ice.png b/src/main/resources/assets/wizardry/textures/gui/element_icon_ice.png new file mode 100644 index 00000000..d9079b6f Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/element_icon_ice.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/element_icon_lightning.png b/src/main/resources/assets/wizardry/textures/gui/element_icon_lightning.png new file mode 100644 index 00000000..287483d3 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/element_icon_lightning.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/element_icon_necromancy.png b/src/main/resources/assets/wizardry/textures/gui/element_icon_necromancy.png new file mode 100644 index 00000000..38bcbc45 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/element_icon_necromancy.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/element_icon_simple.png b/src/main/resources/assets/wizardry/textures/gui/element_icon_simple.png new file mode 100644 index 00000000..9531eb9a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/element_icon_simple.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/element_icon_sorcery.png b/src/main/resources/assets/wizardry/textures/gui/element_icon_sorcery.png new file mode 100644 index 00000000..3900024f Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/element_icon_sorcery.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/empty_slot_crystal.png b/src/main/resources/assets/wizardry/textures/gui/empty_slot_crystal.png new file mode 100644 index 00000000..a4893196 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/empty_slot_crystal.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/empty_slot_upgrade.png b/src/main/resources/assets/wizardry/textures/gui/empty_slot_upgrade.png new file mode 100644 index 00000000..22939c7e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/empty_slot_upgrade.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/frost_icon.png b/src/main/resources/assets/wizardry/textures/gui/frost_icon.png new file mode 100644 index 00000000..e7b63b11 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/frost_icon.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/frost_overlay.png b/src/main/resources/assets/wizardry/textures/gui/frost_overlay.png new file mode 100644 index 00000000..cf4166b0 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/frost_overlay.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/handbook.png b/src/main/resources/assets/wizardry/textures/gui/handbook.png new file mode 100644 index 00000000..9e8aa947 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/handbook.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/handbook_recipes.png b/src/main/resources/assets/wizardry/textures/gui/handbook_recipes.png new file mode 100644 index 00000000..6aab80dc Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/handbook_recipes.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/logo.png b/src/main/resources/assets/wizardry/textures/gui/logo.png new file mode 100644 index 00000000..aae8e46f Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/logo.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/ore_picture.png b/src/main/resources/assets/wizardry/textures/gui/ore_picture.png new file mode 100644 index 00000000..6cddbc7e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/ore_picture.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/potion_icons.png b/src/main/resources/assets/wizardry/textures/gui/potion_icons.png new file mode 100644 index 00000000..d31d92c6 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/potion_icons.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/sixth_sense_overlay.png b/src/main/resources/assets/wizardry/textures/gui/sixth_sense_overlay.png new file mode 100644 index 00000000..ba327477 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/sixth_sense_overlay.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/spell_hud.png b/src/main/resources/assets/wizardry/textures/gui/spell_hud.png new file mode 100644 index 00000000..8fb74e4e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/spell_hud.png differ diff --git a/src/main/resources/assets/wizardry/textures/gui/spellbook.png b/src/main/resources/assets/wizardry/textures/gui/spellbook.png new file mode 100644 index 00000000..ccb7aef4 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/gui/spellbook.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/Thumbs.db b/src/main/resources/assets/wizardry/textures/items/Thumbs.db new file mode 100644 index 00000000..ec2ec131 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/Thumbs.db differ diff --git a/src/main/resources/assets/wizardry/textures/items/arcane_tome.png b/src/main/resources/assets/wizardry/textures/items/arcane_tome.png new file mode 100644 index 00000000..a0280dcc Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/arcane_tome.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/armour_upgrade.png b/src/main/resources/assets/wizardry/textures/items/armour_upgrade.png new file mode 100644 index 00000000..19a21c75 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/armour_upgrade.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/firebomb.png b/src/main/resources/assets/wizardry/textures/items/firebomb.png new file mode 100644 index 00000000..60f6b11a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/firebomb.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/flaming_axe.png b/src/main/resources/assets/wizardry/textures/items/flaming_axe.png new file mode 100644 index 00000000..db3d60f4 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/flaming_axe.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/flaming_axe.png.mcmeta b/src/main/resources/assets/wizardry/textures/items/flaming_axe.png.mcmeta new file mode 100644 index 00000000..eb5c833c --- /dev/null +++ b/src/main/resources/assets/wizardry/textures/items/flaming_axe.png.mcmeta @@ -0,0 +1,12 @@ +{ + "animation": { + + "frametime": 2, + + "frames": [ +0, + 1, 2, 3, 4, 5, 6, 7] + + } + +} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/textures/items/frost_axe.png b/src/main/resources/assets/wizardry/textures/items/frost_axe.png new file mode 100644 index 00000000..bcb6fd03 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/frost_axe.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/identification_scroll.png b/src/main/resources/assets/wizardry/textures/items/identification_scroll.png new file mode 100644 index 00000000..9a6e7afe Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/identification_scroll.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/magic_crystal.png b/src/main/resources/assets/wizardry/textures/items/magic_crystal.png new file mode 100644 index 00000000..9035e562 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/magic_crystal.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/magic_silk.png b/src/main/resources/assets/wizardry/textures/items/magic_silk.png new file mode 100644 index 00000000..65c2fb2b Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/magic_silk.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/mana_flask.png b/src/main/resources/assets/wizardry/textures/items/mana_flask.png new file mode 100644 index 00000000..d8194312 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/mana_flask.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/poison_bomb.png b/src/main/resources/assets/wizardry/textures/items/poison_bomb.png new file mode 100644 index 00000000..c80e8d86 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/poison_bomb.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/scroll.png b/src/main/resources/assets/wizardry/textures/items/scroll.png new file mode 100644 index 00000000..13d56d1b Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/scroll.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/smoke_bomb.png b/src/main/resources/assets/wizardry/textures/items/smoke_bomb.png new file mode 100644 index 00000000..d98dba0e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/smoke_bomb.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/spectral_boots.png b/src/main/resources/assets/wizardry/textures/items/spectral_boots.png new file mode 100644 index 00000000..4ca991da Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/spectral_boots.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/spectral_bow_pulling_0.png b/src/main/resources/assets/wizardry/textures/items/spectral_bow_pulling_0.png new file mode 100644 index 00000000..f089e38d Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/spectral_bow_pulling_0.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/spectral_bow_pulling_1.png b/src/main/resources/assets/wizardry/textures/items/spectral_bow_pulling_1.png new file mode 100644 index 00000000..8f89801a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/spectral_bow_pulling_1.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/spectral_bow_pulling_2.png b/src/main/resources/assets/wizardry/textures/items/spectral_bow_pulling_2.png new file mode 100644 index 00000000..ff4df979 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/spectral_bow_pulling_2.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/spectral_bow_standby.png b/src/main/resources/assets/wizardry/textures/items/spectral_bow_standby.png new file mode 100644 index 00000000..9be035b3 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/spectral_bow_standby.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/spectral_chestplate.png b/src/main/resources/assets/wizardry/textures/items/spectral_chestplate.png new file mode 100644 index 00000000..0b4917de Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/spectral_chestplate.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/spectral_helmet.png b/src/main/resources/assets/wizardry/textures/items/spectral_helmet.png new file mode 100644 index 00000000..cd8b2e5d Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/spectral_helmet.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/spectral_leggings.png b/src/main/resources/assets/wizardry/textures/items/spectral_leggings.png new file mode 100644 index 00000000..20a2d845 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/spectral_leggings.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/spectral_pickaxe.png b/src/main/resources/assets/wizardry/textures/items/spectral_pickaxe.png new file mode 100644 index 00000000..1f49fdce Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/spectral_pickaxe.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/spectral_sword.png b/src/main/resources/assets/wizardry/textures/items/spectral_sword.png new file mode 100644 index 00000000..95b2dfae Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/spectral_sword.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/spell_book.png b/src/main/resources/assets/wizardry/textures/items/spell_book.png new file mode 100644 index 00000000..2043fa4a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/spell_book.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/upgrade_attunement.png b/src/main/resources/assets/wizardry/textures/items/upgrade_attunement.png new file mode 100644 index 00000000..bfaad401 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/upgrade_attunement.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/upgrade_blast.png b/src/main/resources/assets/wizardry/textures/items/upgrade_blast.png new file mode 100644 index 00000000..d3e98d96 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/upgrade_blast.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/upgrade_condenser.png b/src/main/resources/assets/wizardry/textures/items/upgrade_condenser.png new file mode 100644 index 00000000..cf456b02 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/upgrade_condenser.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/upgrade_cooldown.png b/src/main/resources/assets/wizardry/textures/items/upgrade_cooldown.png new file mode 100644 index 00000000..4a4bcb24 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/upgrade_cooldown.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/upgrade_duration.png b/src/main/resources/assets/wizardry/textures/items/upgrade_duration.png new file mode 100644 index 00000000..c79d0923 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/upgrade_duration.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/upgrade_range.png b/src/main/resources/assets/wizardry/textures/items/upgrade_range.png new file mode 100644 index 00000000..b7a21cec Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/upgrade_range.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/upgrade_siphon.png b/src/main/resources/assets/wizardry/textures/items/upgrade_siphon.png new file mode 100644 index 00000000..ff7bbd37 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/upgrade_siphon.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/upgrade_storage.png b/src/main/resources/assets/wizardry/textures/items/upgrade_storage.png new file mode 100644 index 00000000..170f6e26 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/upgrade_storage.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_advanced.png b/src/main/resources/assets/wizardry/textures/items/wand_advanced.png new file mode 100644 index 00000000..02a3044e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_advanced.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_advanced_earth.png b/src/main/resources/assets/wizardry/textures/items/wand_advanced_earth.png new file mode 100644 index 00000000..940da730 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_advanced_earth.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_advanced_fire.png b/src/main/resources/assets/wizardry/textures/items/wand_advanced_fire.png new file mode 100644 index 00000000..2acb5a57 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_advanced_fire.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_advanced_healing.png b/src/main/resources/assets/wizardry/textures/items/wand_advanced_healing.png new file mode 100644 index 00000000..4bd169d8 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_advanced_healing.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_advanced_ice.png b/src/main/resources/assets/wizardry/textures/items/wand_advanced_ice.png new file mode 100644 index 00000000..653e67ed Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_advanced_ice.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_advanced_lightning.png b/src/main/resources/assets/wizardry/textures/items/wand_advanced_lightning.png new file mode 100644 index 00000000..e3932007 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_advanced_lightning.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_advanced_necromancy.png b/src/main/resources/assets/wizardry/textures/items/wand_advanced_necromancy.png new file mode 100644 index 00000000..89ba55c2 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_advanced_necromancy.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_advanced_sorcery.png b/src/main/resources/assets/wizardry/textures/items/wand_advanced_sorcery.png new file mode 100644 index 00000000..135cf840 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_advanced_sorcery.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_apprentice.png b/src/main/resources/assets/wizardry/textures/items/wand_apprentice.png new file mode 100644 index 00000000..3b39afe1 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_apprentice.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_apprentice_earth.png b/src/main/resources/assets/wizardry/textures/items/wand_apprentice_earth.png new file mode 100644 index 00000000..4b25eb50 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_apprentice_earth.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_apprentice_fire.png b/src/main/resources/assets/wizardry/textures/items/wand_apprentice_fire.png new file mode 100644 index 00000000..ab83f220 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_apprentice_fire.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_apprentice_healing.png b/src/main/resources/assets/wizardry/textures/items/wand_apprentice_healing.png new file mode 100644 index 00000000..5633f153 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_apprentice_healing.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_apprentice_ice.png b/src/main/resources/assets/wizardry/textures/items/wand_apprentice_ice.png new file mode 100644 index 00000000..e596af7f Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_apprentice_ice.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_apprentice_lightning.png b/src/main/resources/assets/wizardry/textures/items/wand_apprentice_lightning.png new file mode 100644 index 00000000..1626715b Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_apprentice_lightning.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_apprentice_necromancy.png b/src/main/resources/assets/wizardry/textures/items/wand_apprentice_necromancy.png new file mode 100644 index 00000000..83f0ded1 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_apprentice_necromancy.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_apprentice_sorcery.png b/src/main/resources/assets/wizardry/textures/items/wand_apprentice_sorcery.png new file mode 100644 index 00000000..9e5f37e3 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_apprentice_sorcery.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_basic.png b/src/main/resources/assets/wizardry/textures/items/wand_basic.png new file mode 100644 index 00000000..3b21bdff Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_basic.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_basic_earth.png b/src/main/resources/assets/wizardry/textures/items/wand_basic_earth.png new file mode 100644 index 00000000..83c3e104 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_basic_earth.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_basic_fire.png b/src/main/resources/assets/wizardry/textures/items/wand_basic_fire.png new file mode 100644 index 00000000..3e07a039 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_basic_fire.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_basic_healing.png b/src/main/resources/assets/wizardry/textures/items/wand_basic_healing.png new file mode 100644 index 00000000..e88fc7d0 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_basic_healing.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_basic_ice.png b/src/main/resources/assets/wizardry/textures/items/wand_basic_ice.png new file mode 100644 index 00000000..9ed786eb Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_basic_ice.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_basic_lightning.png b/src/main/resources/assets/wizardry/textures/items/wand_basic_lightning.png new file mode 100644 index 00000000..8d6a6118 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_basic_lightning.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_basic_necromancy.png b/src/main/resources/assets/wizardry/textures/items/wand_basic_necromancy.png new file mode 100644 index 00000000..7ba5ad30 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_basic_necromancy.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_basic_sorcery.png b/src/main/resources/assets/wizardry/textures/items/wand_basic_sorcery.png new file mode 100644 index 00000000..042487ab Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_basic_sorcery.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_master.png b/src/main/resources/assets/wizardry/textures/items/wand_master.png new file mode 100644 index 00000000..7bd7f7d7 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_master.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_master_earth.png b/src/main/resources/assets/wizardry/textures/items/wand_master_earth.png new file mode 100644 index 00000000..6b90ba21 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_master_earth.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_master_fire.png b/src/main/resources/assets/wizardry/textures/items/wand_master_fire.png new file mode 100644 index 00000000..923d6d44 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_master_fire.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_master_healing.png b/src/main/resources/assets/wizardry/textures/items/wand_master_healing.png new file mode 100644 index 00000000..a67c6dca Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_master_healing.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_master_ice.png b/src/main/resources/assets/wizardry/textures/items/wand_master_ice.png new file mode 100644 index 00000000..529cf932 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_master_ice.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_master_lightning.png b/src/main/resources/assets/wizardry/textures/items/wand_master_lightning.png new file mode 100644 index 00000000..e3f1dad0 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_master_lightning.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_master_necromancy.png b/src/main/resources/assets/wizardry/textures/items/wand_master_necromancy.png new file mode 100644 index 00000000..dd729046 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_master_necromancy.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wand_master_sorcery.png b/src/main/resources/assets/wizardry/textures/items/wand_master_sorcery.png new file mode 100644 index 00000000..0c19262e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wand_master_sorcery.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_boots.png b/src/main/resources/assets/wizardry/textures/items/wizard_boots.png new file mode 100644 index 00000000..87014fcd Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_boots.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_boots_earth.png b/src/main/resources/assets/wizardry/textures/items/wizard_boots_earth.png new file mode 100644 index 00000000..3c87329d Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_boots_earth.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_boots_fire.png b/src/main/resources/assets/wizardry/textures/items/wizard_boots_fire.png new file mode 100644 index 00000000..a72d1d62 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_boots_fire.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_boots_healing.png b/src/main/resources/assets/wizardry/textures/items/wizard_boots_healing.png new file mode 100644 index 00000000..65f74b28 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_boots_healing.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_boots_ice.png b/src/main/resources/assets/wizardry/textures/items/wizard_boots_ice.png new file mode 100644 index 00000000..13df533a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_boots_ice.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_boots_lightning.png b/src/main/resources/assets/wizardry/textures/items/wizard_boots_lightning.png new file mode 100644 index 00000000..6800adc1 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_boots_lightning.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_boots_necromancy.png b/src/main/resources/assets/wizardry/textures/items/wizard_boots_necromancy.png new file mode 100644 index 00000000..c92f5be7 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_boots_necromancy.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_boots_sorcery.png b/src/main/resources/assets/wizardry/textures/items/wizard_boots_sorcery.png new file mode 100644 index 00000000..ed2857f2 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_boots_sorcery.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_handbook.png b/src/main/resources/assets/wizardry/textures/items/wizard_handbook.png new file mode 100644 index 00000000..aa0dffee Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_handbook.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_hat.png b/src/main/resources/assets/wizardry/textures/items/wizard_hat.png new file mode 100644 index 00000000..9c8bba35 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_hat.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_hat_earth.png b/src/main/resources/assets/wizardry/textures/items/wizard_hat_earth.png new file mode 100644 index 00000000..7022a917 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_hat_earth.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_hat_fire.png b/src/main/resources/assets/wizardry/textures/items/wizard_hat_fire.png new file mode 100644 index 00000000..91ec1488 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_hat_fire.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_hat_healing.png b/src/main/resources/assets/wizardry/textures/items/wizard_hat_healing.png new file mode 100644 index 00000000..519c0b92 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_hat_healing.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_hat_ice.png b/src/main/resources/assets/wizardry/textures/items/wizard_hat_ice.png new file mode 100644 index 00000000..5502d876 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_hat_ice.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_hat_lightning.png b/src/main/resources/assets/wizardry/textures/items/wizard_hat_lightning.png new file mode 100644 index 00000000..2bdf692d Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_hat_lightning.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_hat_necromancy.png b/src/main/resources/assets/wizardry/textures/items/wizard_hat_necromancy.png new file mode 100644 index 00000000..c47e23d9 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_hat_necromancy.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_hat_sorcery.png b/src/main/resources/assets/wizardry/textures/items/wizard_hat_sorcery.png new file mode 100644 index 00000000..d385a6d5 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_hat_sorcery.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_leggings.png b/src/main/resources/assets/wizardry/textures/items/wizard_leggings.png new file mode 100644 index 00000000..18e41a07 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_leggings.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_leggings_earth.png b/src/main/resources/assets/wizardry/textures/items/wizard_leggings_earth.png new file mode 100644 index 00000000..7b6a2b4d Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_leggings_earth.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_leggings_fire.png b/src/main/resources/assets/wizardry/textures/items/wizard_leggings_fire.png new file mode 100644 index 00000000..95c79c10 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_leggings_fire.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_leggings_healing.png b/src/main/resources/assets/wizardry/textures/items/wizard_leggings_healing.png new file mode 100644 index 00000000..61b6b774 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_leggings_healing.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_leggings_ice.png b/src/main/resources/assets/wizardry/textures/items/wizard_leggings_ice.png new file mode 100644 index 00000000..220d7c18 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_leggings_ice.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_leggings_lightning.png b/src/main/resources/assets/wizardry/textures/items/wizard_leggings_lightning.png new file mode 100644 index 00000000..40c031e3 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_leggings_lightning.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_leggings_necromancy.png b/src/main/resources/assets/wizardry/textures/items/wizard_leggings_necromancy.png new file mode 100644 index 00000000..ea0d6222 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_leggings_necromancy.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_leggings_sorcery.png b/src/main/resources/assets/wizardry/textures/items/wizard_leggings_sorcery.png new file mode 100644 index 00000000..6009ebbc Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_leggings_sorcery.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_robe.png b/src/main/resources/assets/wizardry/textures/items/wizard_robe.png new file mode 100644 index 00000000..84ad99e2 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_robe.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_robe_earth.png b/src/main/resources/assets/wizardry/textures/items/wizard_robe_earth.png new file mode 100644 index 00000000..e0c8e022 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_robe_earth.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_robe_fire.png b/src/main/resources/assets/wizardry/textures/items/wizard_robe_fire.png new file mode 100644 index 00000000..6ab1acb8 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_robe_fire.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_robe_healing.png b/src/main/resources/assets/wizardry/textures/items/wizard_robe_healing.png new file mode 100644 index 00000000..e72cc3f1 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_robe_healing.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_robe_ice.png b/src/main/resources/assets/wizardry/textures/items/wizard_robe_ice.png new file mode 100644 index 00000000..617a3001 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_robe_ice.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_robe_lightning.png b/src/main/resources/assets/wizardry/textures/items/wizard_robe_lightning.png new file mode 100644 index 00000000..fd873d51 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_robe_lightning.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_robe_necromancy.png b/src/main/resources/assets/wizardry/textures/items/wizard_robe_necromancy.png new file mode 100644 index 00000000..cdb6f268 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_robe_necromancy.png differ diff --git a/src/main/resources/assets/wizardry/textures/items/wizard_robe_sorcery.png b/src/main/resources/assets/wizardry/textures/items/wizard_robe_sorcery.png new file mode 100644 index 00000000..1e71d862 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/items/wizard_robe_sorcery.png differ diff --git a/src/main/resources/assets/wizardry/textures/particle/ice_particles.png b/src/main/resources/assets/wizardry/textures/particle/ice_particles.png new file mode 100644 index 00000000..96c99ead Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/particle/ice_particles.png differ diff --git a/src/main/resources/assets/wizardry/textures/particle/leaf_particles.png b/src/main/resources/assets/wizardry/textures/particle/leaf_particles.png new file mode 100644 index 00000000..d24657c9 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/particle/leaf_particles.png differ diff --git a/src/main/resources/assets/wizardry/textures/particle/lightning_particles.png b/src/main/resources/assets/wizardry/textures/particle/lightning_particles.png new file mode 100644 index 00000000..94dc49c5 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/particle/lightning_particles.png differ diff --git a/src/main/resources/assets/wizardry/textures/particle/path_particles.png b/src/main/resources/assets/wizardry/textures/particle/path_particles.png new file mode 100644 index 00000000..ab02959d Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/particle/path_particles.png differ diff --git a/src/main/resources/assets/wizardry/textures/particle/snow_particles.png b/src/main/resources/assets/wizardry/textures/particle/snow_particles.png new file mode 100644 index 00000000..9d636de1 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/particle/snow_particles.png differ diff --git a/src/main/resources/assets/wizardry/textures/particle/sparkle_particles.png b/src/main/resources/assets/wizardry/textures/particle/sparkle_particles.png new file mode 100644 index 00000000..d767eac3 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/particle/sparkle_particles.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/agility.png b/src/main/resources/assets/wizardry/textures/spells/agility.png new file mode 100644 index 00000000..a037f78a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/agility.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/arc.png b/src/main/resources/assets/wizardry/textures/spells/arc.png new file mode 100644 index 00000000..ae5fc756 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/arc.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/arcane_jammer.png b/src/main/resources/assets/wizardry/textures/spells/arcane_jammer.png new file mode 100644 index 00000000..b83d8692 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/arcane_jammer.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/arrow_rain.png b/src/main/resources/assets/wizardry/textures/spells/arrow_rain.png new file mode 100644 index 00000000..e1df005c Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/arrow_rain.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/banish.png b/src/main/resources/assets/wizardry/textures/spells/banish.png new file mode 100644 index 00000000..c321fc86 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/banish.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/black_hole.png b/src/main/resources/assets/wizardry/textures/spells/black_hole.png new file mode 100644 index 00000000..f48a1f5d Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/black_hole.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/blink.png b/src/main/resources/assets/wizardry/textures/spells/blink.png new file mode 100644 index 00000000..77af4634 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/blink.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/blizzard.png b/src/main/resources/assets/wizardry/textures/spells/blizzard.png new file mode 100644 index 00000000..543cae08 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/blizzard.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/bubble.png b/src/main/resources/assets/wizardry/textures/spells/bubble.png new file mode 100644 index 00000000..206f2e41 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/bubble.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/chain_lightning.png b/src/main/resources/assets/wizardry/textures/spells/chain_lightning.png new file mode 100644 index 00000000..6fbc732a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/chain_lightning.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/clairvoyance.png b/src/main/resources/assets/wizardry/textures/spells/clairvoyance.png new file mode 100644 index 00000000..81ac7356 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/clairvoyance.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/cobwebs.png b/src/main/resources/assets/wizardry/textures/spells/cobwebs.png new file mode 100644 index 00000000..8d949207 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/cobwebs.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/conjure_armour.png b/src/main/resources/assets/wizardry/textures/spells/conjure_armour.png new file mode 100644 index 00000000..712476c5 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/conjure_armour.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/conjure_bow.png b/src/main/resources/assets/wizardry/textures/spells/conjure_bow.png new file mode 100644 index 00000000..64197180 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/conjure_bow.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/conjure_pickaxe.png b/src/main/resources/assets/wizardry/textures/spells/conjure_pickaxe.png new file mode 100644 index 00000000..e7a7febc Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/conjure_pickaxe.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/conjure_sword.png b/src/main/resources/assets/wizardry/textures/spells/conjure_sword.png new file mode 100644 index 00000000..cc78cf8e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/conjure_sword.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/cure_effects.png b/src/main/resources/assets/wizardry/textures/spells/cure_effects.png new file mode 100644 index 00000000..5a2ce1ba Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/cure_effects.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/curse_of_soulbinding.png b/src/main/resources/assets/wizardry/textures/spells/curse_of_soulbinding.png new file mode 100644 index 00000000..a788ba70 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/curse_of_soulbinding.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/darkness_orb.png b/src/main/resources/assets/wizardry/textures/spells/darkness_orb.png new file mode 100644 index 00000000..2be9852d Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/darkness_orb.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/darkvision.png b/src/main/resources/assets/wizardry/textures/spells/darkvision.png new file mode 100644 index 00000000..74665d80 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/darkvision.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/dart.png b/src/main/resources/assets/wizardry/textures/spells/dart.png new file mode 100644 index 00000000..a91aa5bb Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/dart.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/decay.png b/src/main/resources/assets/wizardry/textures/spells/decay.png new file mode 100644 index 00000000..4eb345dd Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/decay.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/decoy.png b/src/main/resources/assets/wizardry/textures/spells/decoy.png new file mode 100644 index 00000000..0db5fcc6 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/decoy.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/detonate.png b/src/main/resources/assets/wizardry/textures/spells/detonate.png new file mode 100644 index 00000000..823f9e4c Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/detonate.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/diamondflesh.png b/src/main/resources/assets/wizardry/textures/spells/diamondflesh.png new file mode 100644 index 00000000..53a0cce9 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/diamondflesh.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/earthquake.png b/src/main/resources/assets/wizardry/textures/spells/earthquake.png new file mode 100644 index 00000000..5cddf1e7 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/earthquake.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/entrapment.png b/src/main/resources/assets/wizardry/textures/spells/entrapment.png new file mode 100644 index 00000000..5f949852 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/entrapment.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/fire_resistance.png b/src/main/resources/assets/wizardry/textures/spells/fire_resistance.png new file mode 100644 index 00000000..5fd495a6 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/fire_resistance.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/fire_sigil.png b/src/main/resources/assets/wizardry/textures/spells/fire_sigil.png new file mode 100644 index 00000000..63b0993d Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/fire_sigil.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/fireball.png b/src/main/resources/assets/wizardry/textures/spells/fireball.png new file mode 100644 index 00000000..58535e06 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/fireball.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/firebolt.png b/src/main/resources/assets/wizardry/textures/spells/firebolt.png new file mode 100644 index 00000000..7c0abb89 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/firebolt.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/firebomb.png b/src/main/resources/assets/wizardry/textures/spells/firebomb.png new file mode 100644 index 00000000..bad4d728 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/firebomb.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/fireskin.png b/src/main/resources/assets/wizardry/textures/spells/fireskin.png new file mode 100644 index 00000000..0b0efbb5 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/fireskin.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/firestorm.png b/src/main/resources/assets/wizardry/textures/spells/firestorm.png new file mode 100644 index 00000000..161cf144 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/firestorm.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/flame_ray.png b/src/main/resources/assets/wizardry/textures/spells/flame_ray.png new file mode 100644 index 00000000..ecded28b Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/flame_ray.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/flaming_axe.png b/src/main/resources/assets/wizardry/textures/spells/flaming_axe.png new file mode 100644 index 00000000..00ba6b6e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/flaming_axe.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/flaming_weapon.png b/src/main/resources/assets/wizardry/textures/spells/flaming_weapon.png new file mode 100644 index 00000000..24b7002b Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/flaming_weapon.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/flight.png b/src/main/resources/assets/wizardry/textures/spells/flight.png new file mode 100644 index 00000000..16aec1c6 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/flight.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/font_of_mana.png b/src/main/resources/assets/wizardry/textures/spells/font_of_mana.png new file mode 100644 index 00000000..f3968427 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/font_of_mana.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/font_of_vitality.png b/src/main/resources/assets/wizardry/textures/spells/font_of_vitality.png new file mode 100644 index 00000000..b07e442c Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/font_of_vitality.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/force_arrow.png b/src/main/resources/assets/wizardry/textures/spells/force_arrow.png new file mode 100644 index 00000000..a388c0bd Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/force_arrow.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/force_orb.png b/src/main/resources/assets/wizardry/textures/spells/force_orb.png new file mode 100644 index 00000000..e87249c4 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/force_orb.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/forcefield.png b/src/main/resources/assets/wizardry/textures/spells/forcefield.png new file mode 100644 index 00000000..8dc0a6d4 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/forcefield.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/forests_curse.png b/src/main/resources/assets/wizardry/textures/spells/forests_curse.png new file mode 100644 index 00000000..c99e4166 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/forests_curse.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/freeze.png b/src/main/resources/assets/wizardry/textures/spells/freeze.png new file mode 100644 index 00000000..4c8329e6 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/freeze.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/freezing_weapon.png b/src/main/resources/assets/wizardry/textures/spells/freezing_weapon.png new file mode 100644 index 00000000..60cce351 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/freezing_weapon.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/frost_axe.png b/src/main/resources/assets/wizardry/textures/spells/frost_axe.png new file mode 100644 index 00000000..e90c6909 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/frost_axe.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/frost_ray.png b/src/main/resources/assets/wizardry/textures/spells/frost_ray.png new file mode 100644 index 00000000..aad2b2c3 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/frost_ray.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/frost_sigil.png b/src/main/resources/assets/wizardry/textures/spells/frost_sigil.png new file mode 100644 index 00000000..5972bd58 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/frost_sigil.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/glide.png b/src/main/resources/assets/wizardry/textures/spells/glide.png new file mode 100644 index 00000000..d4890ccb Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/glide.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/greater_fireball.png b/src/main/resources/assets/wizardry/textures/spells/greater_fireball.png new file mode 100644 index 00000000..e465bb0a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/greater_fireball.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/greater_heal.png b/src/main/resources/assets/wizardry/textures/spells/greater_heal.png new file mode 100644 index 00000000..9f51e42f Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/greater_heal.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/group_heal.png b/src/main/resources/assets/wizardry/textures/spells/group_heal.png new file mode 100644 index 00000000..3c99c033 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/group_heal.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/growth_aura.png b/src/main/resources/assets/wizardry/textures/spells/growth_aura.png new file mode 100644 index 00000000..15c4f9af Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/growth_aura.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/hailstorm.png b/src/main/resources/assets/wizardry/textures/spells/hailstorm.png new file mode 100644 index 00000000..e282e27a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/hailstorm.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/heal.png b/src/main/resources/assets/wizardry/textures/spells/heal.png new file mode 100644 index 00000000..1b5f5f12 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/heal.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/heal_ally.png b/src/main/resources/assets/wizardry/textures/spells/heal_ally.png new file mode 100644 index 00000000..d8e24b69 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/heal_ally.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/healing_aura.png b/src/main/resources/assets/wizardry/textures/spells/healing_aura.png new file mode 100644 index 00000000..7118ebab Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/healing_aura.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/homing_spark.png b/src/main/resources/assets/wizardry/textures/spells/homing_spark.png new file mode 100644 index 00000000..c3cf0ded Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/homing_spark.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/ice_age.png b/src/main/resources/assets/wizardry/textures/spells/ice_age.png new file mode 100644 index 00000000..18c231af Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/ice_age.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/ice_charge.png b/src/main/resources/assets/wizardry/textures/spells/ice_charge.png new file mode 100644 index 00000000..128342d4 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/ice_charge.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/ice_lance.png b/src/main/resources/assets/wizardry/textures/spells/ice_lance.png new file mode 100644 index 00000000..1674a8fd Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/ice_lance.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/ice_shard.png b/src/main/resources/assets/wizardry/textures/spells/ice_shard.png new file mode 100644 index 00000000..62f80aee Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/ice_shard.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/ice_shroud.png b/src/main/resources/assets/wizardry/textures/spells/ice_shroud.png new file mode 100644 index 00000000..004496e8 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/ice_shroud.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/ice_spikes.png b/src/main/resources/assets/wizardry/textures/spells/ice_spikes.png new file mode 100644 index 00000000..87f5e23e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/ice_spikes.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/ice_statue.png b/src/main/resources/assets/wizardry/textures/spells/ice_statue.png new file mode 100644 index 00000000..06f9189b Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/ice_statue.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/ignite.png b/src/main/resources/assets/wizardry/textures/spells/ignite.png new file mode 100644 index 00000000..6403bc84 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/ignite.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/imbue_weapon.png b/src/main/resources/assets/wizardry/textures/spells/imbue_weapon.png new file mode 100644 index 00000000..a17a3966 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/imbue_weapon.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/intimidate.png b/src/main/resources/assets/wizardry/textures/spells/intimidate.png new file mode 100644 index 00000000..2981d430 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/intimidate.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/invigorating_presence.png b/src/main/resources/assets/wizardry/textures/spells/invigorating_presence.png new file mode 100644 index 00000000..950c64cc Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/invigorating_presence.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/invisibility.png b/src/main/resources/assets/wizardry/textures/spells/invisibility.png new file mode 100644 index 00000000..da70bcae Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/invisibility.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/invoke_weather.png b/src/main/resources/assets/wizardry/textures/spells/invoke_weather.png new file mode 100644 index 00000000..696fa95a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/invoke_weather.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/ironflesh.png b/src/main/resources/assets/wizardry/textures/spells/ironflesh.png new file mode 100644 index 00000000..b059a7fc Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/ironflesh.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/leap.png b/src/main/resources/assets/wizardry/textures/spells/leap.png new file mode 100644 index 00000000..70c9e2e2 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/leap.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/levitation.png b/src/main/resources/assets/wizardry/textures/spells/levitation.png new file mode 100644 index 00000000..f49b2809 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/levitation.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/life_drain.png b/src/main/resources/assets/wizardry/textures/spells/life_drain.png new file mode 100644 index 00000000..a84afb52 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/life_drain.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/light.png b/src/main/resources/assets/wizardry/textures/spells/light.png new file mode 100644 index 00000000..9c100cc4 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/light.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/lightning_arrow.png b/src/main/resources/assets/wizardry/textures/spells/lightning_arrow.png new file mode 100644 index 00000000..f233b380 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/lightning_arrow.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/lightning_bolt.png b/src/main/resources/assets/wizardry/textures/spells/lightning_bolt.png new file mode 100644 index 00000000..7ad6746e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/lightning_bolt.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/lightning_disc.png b/src/main/resources/assets/wizardry/textures/spells/lightning_disc.png new file mode 100644 index 00000000..1052aaa1 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/lightning_disc.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/lightning_hammer.png b/src/main/resources/assets/wizardry/textures/spells/lightning_hammer.png new file mode 100644 index 00000000..91332331 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/lightning_hammer.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/lightning_pulse.png b/src/main/resources/assets/wizardry/textures/spells/lightning_pulse.png new file mode 100644 index 00000000..967ba43a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/lightning_pulse.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/lightning_ray.png b/src/main/resources/assets/wizardry/textures/spells/lightning_ray.png new file mode 100644 index 00000000..5b7f8dd7 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/lightning_ray.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/lightning_sigil.png b/src/main/resources/assets/wizardry/textures/spells/lightning_sigil.png new file mode 100644 index 00000000..961bc7ae Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/lightning_sigil.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/lightning_web.png b/src/main/resources/assets/wizardry/textures/spells/lightning_web.png new file mode 100644 index 00000000..9c89c25f Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/lightning_web.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/magic_missile.png b/src/main/resources/assets/wizardry/textures/spells/magic_missile.png new file mode 100644 index 00000000..e176fbe5 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/magic_missile.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/metamorphosis.png b/src/main/resources/assets/wizardry/textures/spells/metamorphosis.png new file mode 100644 index 00000000..f0a2113b Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/metamorphosis.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/meteor.png b/src/main/resources/assets/wizardry/textures/spells/meteor.png new file mode 100644 index 00000000..09c1f47a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/meteor.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/mind_control.png b/src/main/resources/assets/wizardry/textures/spells/mind_control.png new file mode 100644 index 00000000..01ac6671 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/mind_control.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/mind_trick.png b/src/main/resources/assets/wizardry/textures/spells/mind_trick.png new file mode 100644 index 00000000..f2eb8fbd Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/mind_trick.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/none.png b/src/main/resources/assets/wizardry/textures/spells/none.png new file mode 100644 index 00000000..efcb09e1 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/none.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/oakflesh.png b/src/main/resources/assets/wizardry/textures/spells/oakflesh.png new file mode 100644 index 00000000..95080a21 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/oakflesh.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/petrify.png b/src/main/resources/assets/wizardry/textures/spells/petrify.png new file mode 100644 index 00000000..4315bb91 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/petrify.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/phase_step.png b/src/main/resources/assets/wizardry/textures/spells/phase_step.png new file mode 100644 index 00000000..e58a26f5 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/phase_step.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/plague_of_darkness.png b/src/main/resources/assets/wizardry/textures/spells/plague_of_darkness.png new file mode 100644 index 00000000..eeb386b6 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/plague_of_darkness.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/pocket_furnace.png b/src/main/resources/assets/wizardry/textures/spells/pocket_furnace.png new file mode 100644 index 00000000..900f5345 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/pocket_furnace.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/pocket_workbench.png b/src/main/resources/assets/wizardry/textures/spells/pocket_workbench.png new file mode 100644 index 00000000..3cc8f8cd Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/pocket_workbench.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/poison.png b/src/main/resources/assets/wizardry/textures/spells/poison.png new file mode 100644 index 00000000..85f147b6 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/poison.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/poison_bomb.png b/src/main/resources/assets/wizardry/textures/spells/poison_bomb.png new file mode 100644 index 00000000..116b079e Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/poison_bomb.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/replenish_hunger.png b/src/main/resources/assets/wizardry/textures/spells/replenish_hunger.png new file mode 100644 index 00000000..6a75b319 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/replenish_hunger.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/ring_of_fire.png b/src/main/resources/assets/wizardry/textures/spells/ring_of_fire.png new file mode 100644 index 00000000..19fca40a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/ring_of_fire.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/shadow_ward.png b/src/main/resources/assets/wizardry/textures/spells/shadow_ward.png new file mode 100644 index 00000000..c8350a6f Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/shadow_ward.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/shield.png b/src/main/resources/assets/wizardry/textures/spells/shield.png new file mode 100644 index 00000000..0b1588b4 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/shield.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/shockwave.png b/src/main/resources/assets/wizardry/textures/spells/shockwave.png new file mode 100644 index 00000000..49f64999 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/shockwave.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/silverfish_swarm.png b/src/main/resources/assets/wizardry/textures/spells/silverfish_swarm.png new file mode 100644 index 00000000..a776fab7 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/silverfish_swarm.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/sixth_sense.png b/src/main/resources/assets/wizardry/textures/spells/sixth_sense.png new file mode 100644 index 00000000..33bafa00 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/sixth_sense.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/slime.png b/src/main/resources/assets/wizardry/textures/spells/slime.png new file mode 100644 index 00000000..1ada6603 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/slime.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/smoke_bomb.png b/src/main/resources/assets/wizardry/textures/spells/smoke_bomb.png new file mode 100644 index 00000000..74ed1234 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/smoke_bomb.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/snare.png b/src/main/resources/assets/wizardry/textures/spells/snare.png new file mode 100644 index 00000000..ce5a1f4a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/snare.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/snowball.png b/src/main/resources/assets/wizardry/textures/spells/snowball.png new file mode 100644 index 00000000..a38ca350 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/snowball.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/spark_bomb.png b/src/main/resources/assets/wizardry/textures/spells/spark_bomb.png new file mode 100644 index 00000000..1ea4084b Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/spark_bomb.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/spectral_pathway.png b/src/main/resources/assets/wizardry/textures/spells/spectral_pathway.png new file mode 100644 index 00000000..307b2393 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/spectral_pathway.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/spider_swarm.png b/src/main/resources/assets/wizardry/textures/spells/spider_swarm.png new file mode 100644 index 00000000..b4b64ebf Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/spider_swarm.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/static_aura.png b/src/main/resources/assets/wizardry/textures/spells/static_aura.png new file mode 100644 index 00000000..eea54a25 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/static_aura.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_blaze.png b/src/main/resources/assets/wizardry/textures/spells/summon_blaze.png new file mode 100644 index 00000000..aa657ab9 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_blaze.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_ice_giant.png b/src/main/resources/assets/wizardry/textures/spells/summon_ice_giant.png new file mode 100644 index 00000000..e8ef025b Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_ice_giant.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_ice_wraith.png b/src/main/resources/assets/wizardry/textures/spells/summon_ice_wraith.png new file mode 100644 index 00000000..6ea7053a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_ice_wraith.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_iron_golem.png b/src/main/resources/assets/wizardry/textures/spells/summon_iron_golem.png new file mode 100644 index 00000000..8c30b082 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_iron_golem.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_lightning_wraith.png b/src/main/resources/assets/wizardry/textures/spells/summon_lightning_wraith.png new file mode 100644 index 00000000..1464dba6 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_lightning_wraith.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_phoenix.png b/src/main/resources/assets/wizardry/textures/spells/summon_phoenix.png new file mode 100644 index 00000000..d6d7a5c9 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_phoenix.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_shadow_wraith.png b/src/main/resources/assets/wizardry/textures/spells/summon_shadow_wraith.png new file mode 100644 index 00000000..1c6313d1 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_shadow_wraith.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_skeleton.png b/src/main/resources/assets/wizardry/textures/spells/summon_skeleton.png new file mode 100644 index 00000000..58582184 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_skeleton.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_skeleton_legion.png b/src/main/resources/assets/wizardry/textures/spells/summon_skeleton_legion.png new file mode 100644 index 00000000..7e10c45f Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_skeleton_legion.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_snow_golem.png b/src/main/resources/assets/wizardry/textures/spells/summon_snow_golem.png new file mode 100644 index 00000000..9144154c Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_snow_golem.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_spirit_horse.png b/src/main/resources/assets/wizardry/textures/spells/summon_spirit_horse.png new file mode 100644 index 00000000..45171297 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_spirit_horse.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_spirit_wolf.png b/src/main/resources/assets/wizardry/textures/spells/summon_spirit_wolf.png new file mode 100644 index 00000000..1c5a2901 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_spirit_wolf.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_storm_elemental.png b/src/main/resources/assets/wizardry/textures/spells/summon_storm_elemental.png new file mode 100644 index 00000000..2e758158 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_storm_elemental.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_wither_skeleton.png b/src/main/resources/assets/wizardry/textures/spells/summon_wither_skeleton.png new file mode 100644 index 00000000..9453859c Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_wither_skeleton.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/summon_zombie.png b/src/main/resources/assets/wizardry/textures/spells/summon_zombie.png new file mode 100644 index 00000000..b55fd8db Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/summon_zombie.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/telekinesis.png b/src/main/resources/assets/wizardry/textures/spells/telekinesis.png new file mode 100644 index 00000000..ff6d2462 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/telekinesis.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/thunderbolt.png b/src/main/resources/assets/wizardry/textures/spells/thunderbolt.png new file mode 100644 index 00000000..c063eb66 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/thunderbolt.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/thunderstorm.png b/src/main/resources/assets/wizardry/textures/spells/thunderstorm.png new file mode 100644 index 00000000..28920c9a Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/thunderstorm.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/tornado.png b/src/main/resources/assets/wizardry/textures/spells/tornado.png new file mode 100644 index 00000000..dffc70b2 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/tornado.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/transience.png b/src/main/resources/assets/wizardry/textures/spells/transience.png new file mode 100644 index 00000000..572777e2 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/transience.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/transportation.png b/src/main/resources/assets/wizardry/textures/spells/transportation.png new file mode 100644 index 00000000..d3452080 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/transportation.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/vanishing_box.png b/src/main/resources/assets/wizardry/textures/spells/vanishing_box.png new file mode 100644 index 00000000..535c2833 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/vanishing_box.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/wall_of_frost.png b/src/main/resources/assets/wizardry/textures/spells/wall_of_frost.png new file mode 100644 index 00000000..308cda1f Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/wall_of_frost.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/water_breathing.png b/src/main/resources/assets/wizardry/textures/spells/water_breathing.png new file mode 100644 index 00000000..76709571 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/water_breathing.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/whirlwind.png b/src/main/resources/assets/wizardry/textures/spells/whirlwind.png new file mode 100644 index 00000000..096840b3 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/whirlwind.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/wither.png b/src/main/resources/assets/wizardry/textures/spells/wither.png new file mode 100644 index 00000000..8ab4fad7 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/wither.png differ diff --git a/src/main/resources/assets/wizardry/textures/spells/wither_skull.png b/src/main/resources/assets/wizardry/textures/spells/wither_skull.png new file mode 100644 index 00000000..84a135a3 Binary files /dev/null and b/src/main/resources/assets/wizardry/textures/spells/wither_skull.png differ diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info new file mode 100644 index 00000000..528e8735 --- /dev/null +++ b/src/main/resources/mcmod.info @@ -0,0 +1,18 @@ +[ +{ + "modid" : "wizardry", + "name" : "Electroblob's Wizardry", + "version" : "2.1.0", + "url" : "https://minecraft.curseforge.com/projects/electroblobs-wizardry", + "credits" : "Made by Electroblob", + "authors" : [ + "Electroblob" + ], + "description" : "Electroblob's Wizardry adds an rpg-style system of magic spells to Minecraft with the aim of being as playable as possible. No crazy constructs, no perk trees, no complex recipes - simply find spell books, cast spells, and master the arcane!", + "logoFile" : "assets/wizardry/textures/gui/logo.png", + "updateUrl" : "", + "parent" : "", + "screenshots": [ + ] +} +]