Miscellaneous setup
This commit is contained in:
@@ -20,3 +20,4 @@ build
|
||||
# other
|
||||
eclipse
|
||||
run
|
||||
misc
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
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.
|
||||
|
||||
-483
@@ -1,483 +0,0 @@
|
||||
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
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
This repository is copyright © Electroblob 2018. The following license is provided to allow you to creatively contribute to and improve the repository.
|
||||
|
||||
This license applies only to the source code and assets present in this repository. The compiled mod (available at https://minecraft.curseforge.com/projects/electroblobs-wizardry) comes under its own separate license, see the aforementioned webpage for details.
|
||||
|
||||
TL;DR: Do what you want with this repo on Github, but ask me first if you want to put it anywhere else or publish your version. Don't try and make money off the repo or anything in it. I'm not responsible if the code breaks things, and if you help me out by submitting a PR, I can use and modify the contents of that PR as long as I give you credit. (This brief summary is non-binding and does not override or replace the full license below.)
|
||||
|
||||
---
|
||||
|
||||
START OF LICENSE
|
||||
|
||||
0. Used terms:
|
||||
|
||||
'Github' - the website github.com, maintained by Github Inc.
|
||||
'Electroblob' - The author of the Minecraft modification Electroblob's Wizardry, also known as 'Electroblob77'
|
||||
'Electroblob's Wizardry', 'Wizardry', 'the mod' - The Minecraft modification Electroblob's Wizardry
|
||||
'Repository' - This repository on Github, containing this license, other informational files, configurations, code and assets which are part of Electroblob's Wizardry
|
||||
'Curseforge' - The webpage https://minecraft.curseforge.com/projects/electroblobs-wizardry
|
||||
'Minecraft forum' - The webpage http://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/2818029-electroblobs-wizardry-the-expandable-rpg-magic-mod
|
||||
'You' - The user of this repository, who may or may not own a Github account
|
||||
|
||||
1. Warranty and usage
|
||||
|
||||
This repository, the source code for the Minecraft modification Electroblob's Wizardry ('Wizardry'), is provided in the hope that it will be useful and/or educational. You are free to download and modify this repository for personal use only. See section 2 for details regarding public sharing of this repository.
|
||||
|
||||
THIS REPOSITORY IS PROVIDED AS-IS WITH NO WARRANTIES, IMPLIED OR OTHERWISE. ELECTROBLOB TAKES NO RESPONSIBILITY FOR ANY DAMAGES INCURRED BY THE USE OR MISUSE OF THIS REPOSITORY.
|
||||
|
||||
2. Derivative works
|
||||
|
||||
In addition to private use as set out in section 1, you are free to fork this repository and modify it as you wish, publically or privately, on Github only, as set out in the Github terms of service (https://help.github.com/articles/github-terms-of-service). This license must be retained, unchanged, in all forked or modified versions. You are encouraged to detail any changes made from the original repository and the author(s) of those changes. Any code or assets that are added remain the intellectual property of their respective authors, and the author of such an addition may do as they wish with that addition, provided that author does not breach the terms of this license.
|
||||
|
||||
3. Distribution
|
||||
|
||||
Distribution, in whole or in part, of compiled versions of wizardry, and distribution of the source code anywhere except on Github, modified or otherwise, is forbidden without advance written permission from Electroblob. Electroblob reserves the right to refuse or withdraw such permission at any time.
|
||||
|
||||
4. Commercial use
|
||||
|
||||
This repository, in whole or in part, and any modifications, copies or forks thereof may not be used for any commercial purposes, including, but not limited to, URL shorteners such as adf.ly, selling copies of or access to code or assets, or selling modified or compiled versions of this repository.
|
||||
|
||||
5. Contributing
|
||||
|
||||
You are encouraged to submit relevant pull requests for this repository. By submitting a pull request, you grant Electroblob permission to use the content thereof, with due credit given, in Wizardry or elsewhere. Electroblob reserves the right to modify or remove code or assets that have been incorporated into Wizardry from accepted pull requests, but the contributor(s) responsible will remain in the mod credits even if their contribution is removed. Credit will be given to contributors of accepted pull requests as follows:
|
||||
|
||||
1. The contributor's name or pseudonym will be listed under 'Contributors' in CREDITS.txt, both in this repository and in any compiled versions of the mod released on curseforge which incorporate the contributor's work.
|
||||
2. The contributor's name or pseudonym will be listed under 'Contributors' on curseforge and on minecraft forum, inside the 'Credits' spoiler.
|
||||
|
||||
For a set of guidelines regarding coding style and submission of pull requests, see guide_for_contributing.txt.
|
||||
|
||||
6. Changes to this license
|
||||
|
||||
This license is subject to change and may be modified by Electroblob at any time. Only the most recent version will apply at any given time. It is the responsibility of the owner of any forked or modified version of this repository to keep their copy of the license up-to-date, and in the case that it is not up-to-date, the license in the original repository belonging to Electroblob overrides any copies.
|
||||
|
||||
END OF LICENSE
|
||||
@@ -1,26 +0,0 @@
|
||||
* 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.
|
||||
@@ -1,10 +0,0 @@
|
||||
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.
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
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.
|
||||
@@ -1 +1,12 @@
|
||||
# Wizardry
|
||||
|
||||

|
||||
|
||||
Source code for Electroblob's Wizardry, a Minecraft mod currently for versions 1.7.10 and 1.10.2. 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!
|
||||
|
||||
<i>This repository is currently under construction. Please check back when it is ready.</i>
|
||||
|
||||
<b>Links</b>
|
||||
|
||||
CurseForge: https://minecraft.curseforge.com/projects/electroblobs-wizardry<br>
|
||||
Minecraft Forum thread: http://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/2818029-electroblobs-wizardry-the-expandable-rpg-magic-mod
|
||||
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
-------------------------------------------
|
||||
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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
||||
Guide for contributing to this repository
|
||||
|
||||
I like my code to be tidy, so it saves me time if yours is too. Here are a few general guidelines for how to submit a good PR:
|
||||
|
||||
- Use proper indentation and try to space your code out nicely. This really goes without saying - select all your code and hit ctrl-i before you submit it, and try to use my spacing if you can (for instance, don't do what the Minecraft source does and put a { on a separate line, it's a waste of space).
|
||||
|
||||
- Commented code is dead code, UNLESS it has an accompanying comment detailing an appropriate reason for it being commented. (something like "DO NOT REMOVE, commented because it will be useful in 1.12" or "DO NOT REMOVE, commented for debugging purposes, uncomment when fixed")
|
||||
|
||||
- Please comment your methods/fields/classes! Preferably using Javadoc, but for small/private methods one line comments are OK. I find it hard enough to remember what I wrote a method for if it isn't commented, let alone trying to work out what yours are for. That said, you should try to name the method/field/class so it's obvious what it is anyway.
|
||||
|
||||
- Follow naming conventions. BLOCK_CAPITALS for constants, CapitalisedWords for classes, camelCase for almost everything else.
|
||||
|
||||
- This isn't code related, but when you submit a PR, please tell me what it does! You don't need much, just something like "Makes item X do X" or "Fixes #162". If you're resolving an issue, put "Fixes", "Closes" or "Resolves" and then the issue number and Github will close it automatically.
|
||||
@@ -17,10 +17,14 @@ import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldSavedData;
|
||||
import net.minecraftforge.common.util.Constants.NBT;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
/** Class responsible for generating and storing the randomised spell names and descriptions for each world, which are
|
||||
* displayed as glyphs using the SGA font renderer.
|
||||
* @since Wizardry 1.1 */
|
||||
@Mod.EventBusSubscriber
|
||||
public class SpellGlyphData extends WorldSavedData {
|
||||
|
||||
public static final String NAME = Wizardry.MODID + "_glyphData";
|
||||
@@ -109,6 +113,8 @@ public class SpellGlyphData extends WorldSavedData {
|
||||
PacketGlyphData.Message msg = new PacketGlyphData.Message(names, descriptions);
|
||||
|
||||
WizardryPacketHandler.net.sendTo(msg, player);
|
||||
|
||||
Wizardry.logger.info("Synchronising spell glyph data for " + player.getName());
|
||||
|
||||
}
|
||||
|
||||
@@ -158,4 +164,13 @@ public class SpellGlyphData extends WorldSavedData {
|
||||
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.enchantment.Imbuement;
|
||||
import electroblob.wizardry.entity.EntityShield;
|
||||
import electroblob.wizardry.entity.living.ISummonedCreature;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.event.SpellCastEvent.Source;
|
||||
import electroblob.wizardry.packet.PacketCastContinuousSpell;
|
||||
import electroblob.wizardry.packet.PacketPlayerSync;
|
||||
import electroblob.wizardry.packet.PacketTransportation;
|
||||
@@ -40,12 +42,20 @@ import net.minecraft.nbt.NBTTagString;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.CapabilityInject;
|
||||
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
|
||||
import net.minecraftforge.common.util.Constants.NBT;
|
||||
import net.minecraftforge.event.AttachCapabilitiesEvent;
|
||||
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
import net.minecraftforge.common.util.INBTSerializable;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
|
||||
/** Capability-based replacement for the old ExtendedPlayer class from 1.7.10. This has been reworked to leave
|
||||
@@ -58,6 +68,7 @@ import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
* @since Wizardry 1.2
|
||||
* @author Electroblob */
|
||||
// On the plus side, having to rethink this class allowed me to clean it up a lot.
|
||||
@Mod.EventBusSubscriber
|
||||
public class WizardData implements INBTSerializable<NBTTagCompound> {
|
||||
|
||||
/** Static instance of what I like to refer to as the capability key. Private because, well, it's internal! */
|
||||
@@ -65,6 +76,7 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
|
||||
@CapabilityInject(WizardData.class)
|
||||
private static final Capability<WizardData> WIZARD_DATA_CAPABILITY = null;
|
||||
|
||||
/** The player this WizardData instance belongs to. */
|
||||
private final EntityPlayer player;
|
||||
|
||||
// This one is still necessary, because I can't override the equip animation for items that aren't from Wizardry.
|
||||
@@ -329,6 +341,30 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
|
||||
WizardryPacketHandler.net.sendToDimension(message, this.player.worldObj.provider.getDimension());
|
||||
}
|
||||
}
|
||||
|
||||
/** Casts the current continuous spell, fires relevant events and updates the castingTick field. */
|
||||
public void updateContinuousSpellCasting(){
|
||||
|
||||
if(this.currentlyCasting != null && this.currentlyCasting.isContinuous){
|
||||
|
||||
if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(player, currentlyCasting, spellModifiers, Source.COMMAND, castingTick))){
|
||||
this.stopCastingContinuousSpell();
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.currentlyCasting.cast(player.worldObj, player, EnumHand.MAIN_HAND, castingTick, this.spellModifiers)
|
||||
&& this.castingTick == 0){
|
||||
// On the first tick casting a continuous spell via commands, SpellCastEvent.Post is fired.
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, currentlyCasting, spellModifiers, Source.COMMAND));
|
||||
}
|
||||
|
||||
castingTick++;
|
||||
|
||||
}else{
|
||||
// Why is this here? Surely castingTick will always be 0 if currentlyCasting is null?
|
||||
this.castingTick = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns whether this player is currently casting a continuous spell via commands. */
|
||||
public boolean isCasting(){
|
||||
@@ -341,8 +377,8 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
|
||||
return currentlyCasting;
|
||||
}
|
||||
|
||||
/** Called from the event handler each time the associated player is updated. */
|
||||
public void update(){
|
||||
/** Called each time the associated player is updated. */
|
||||
private void update(){
|
||||
|
||||
if(this.selectedMinion != null && this.selectedMinion.get() == null) this.selectedMinion = null;
|
||||
|
||||
@@ -363,12 +399,8 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
|
||||
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;
|
||||
}
|
||||
|
||||
updateContinuousSpellCasting();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -483,6 +515,52 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
|
||||
@SubscribeEvent
|
||||
// The type parameter here has to be Entity, not EntityPlayer, or the event won't get fired.
|
||||
public static void onCapabilityLoad(AttachCapabilitiesEvent<Entity> 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 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();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
|
||||
if(event.getEntityLiving() instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getEntityLiving();
|
||||
|
||||
if(WizardData.get(player) != null){
|
||||
WizardData.get(player).update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 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
|
||||
|
||||
@@ -73,8 +73,6 @@ public class Wizardry {
|
||||
* 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!
|
||||
|
||||
|
||||
@@ -1,72 +1,47 @@
|
||||
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.event.DiscoverSpellEvent;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
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.SpellModifiers;
|
||||
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;
|
||||
@@ -74,73 +49,41 @@ 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.common.MinecraftForge;
|
||||
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
|
||||
// Better ways of organising event handling:
|
||||
// - Split it into several separate handlers, each with a logical area to deal with (see Astral Sorcery)
|
||||
// - Remove most of the stuff, and have individual spell, item, block, entity, etc. classes handle their own events
|
||||
// (see Botania)
|
||||
// - Keep all the methods, but delegate near-repeated behaviours to their individual spell/potion/whatever classes
|
||||
// (TGG does this, apparently)
|
||||
// Incidentally, Twilight Forest still has one big event handler class - and a comment about how it's so long...!
|
||||
|
||||
// In the end, I decided the most pragmatic solution was to either use the second option or keep things how they were,
|
||||
// whichever will result in more readable/easily maintainable code in each case. To keep in line with the modularity
|
||||
// of the spell system, the goal is to have nothing in here which relates to a specific spell (mostly done, but a few
|
||||
// things remain).
|
||||
|
||||
/**
|
||||
* Wizardry's main event handler class for common code.
|
||||
* As of Wizardry 2.1, most of the code in this class has been relocated somewhere sensible, leaving only a few
|
||||
* miscellaneous things that don't make much sense anywhere else, or that are better kept together. Previously, this was
|
||||
* a gigantic class with about half of the entire mod's logic in it!
|
||||
* @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<Entity> 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",
|
||||
@@ -152,7 +95,7 @@ public final class WizardryEventHandler {
|
||||
"minecraft:chests/stronghold_library",
|
||||
"minecraft:chests/igloo_chest"
|
||||
};
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLootTableLoadEvent(LootTableLoadEvent event){
|
||||
if(Wizardry.settings.generateLoot){
|
||||
@@ -172,6 +115,60 @@ public final class WizardryEventHandler {
|
||||
return new LootEntryTable(new ResourceLocation(name), weight, 0, new LootCondition[0], Wizardry.MODID + "_additive_entry");
|
||||
}
|
||||
|
||||
@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 onSpellCastPreEvent(SpellCastEvent.Pre event){
|
||||
// If a spell is disabled in the config, it will not work.
|
||||
if(!event.getSpell().isEnabled()){
|
||||
if(!event.getEntityLiving().worldObj.isRemote)
|
||||
event.getEntity().addChatMessage(new TextComponentTranslation("spell.disabled", event.getSpell().getNameForTranslationFormatted()));
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onSpellCastPostEvent(SpellCastEvent.Post event){
|
||||
|
||||
// Spell discovery (only players can discover spells, obviously)
|
||||
if(event.getEntity() instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getEntity();
|
||||
|
||||
WizardData data = WizardData.get(player);
|
||||
|
||||
if(data != null){
|
||||
// Data is updated on both sides (This line was added client-side to fix a bug back in 1.1.3, so now
|
||||
// it's in common code, which is nice!)
|
||||
// Short-circuiting AND means that discoverSpell is only called if the event isn't cancelled.
|
||||
if(!MinecraftForge.EVENT_BUS.post(new DiscoverSpellEvent(player, event.getSpell(),
|
||||
DiscoverSpellEvent.Source.CASTING)) && data.discoverSpell(event.getSpell())){
|
||||
|
||||
// If the spell wasn't already discovered, other stuff happens:
|
||||
if(event.getSource() == SpellCastEvent.Source.COMMAND){
|
||||
// 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(!event.getSpell().doesSpellRequirePacket()) data.sync();
|
||||
|
||||
}else if(!event.getEntity().worldObj.isRemote && !player.capabilities.isCreativeMode
|
||||
&& Wizardry.settings.discoveryMode){
|
||||
// Sound and text only happen server-side, in survival, with discovery mode on, and only when
|
||||
// the spell wasn't cast using commands.
|
||||
WizardryUtilities.playSoundAtPlayer(player, SoundEvents.ENTITY_PLAYER_LEVELUP, 1.25f, 1);
|
||||
player.addChatMessage(new TextComponentTranslation("spell.discover", event.getSpell().getNameForTranslationFormatted()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
@@ -197,115 +194,39 @@ public final class WizardryEventHandler {
|
||||
@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.
|
||||
// This needs 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
|
||||
// Retaliatory effects
|
||||
// These are better off here because the revenge effects are pretty similar, and I'd rather keep the (lengthy)
|
||||
// if statement in one place.
|
||||
if(event.getSource() != null && event.getSource().getEntity() instanceof EntityLivingBase
|
||||
&& !event.getSource().isProjectile() && !(event.getSource() instanceof IElementalDamage &&
|
||||
((IElementalDamage)event.getSource()).isRetaliatory())){
|
||||
|
||||
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);
|
||||
}
|
||||
// Fireskin
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.fireskin)
|
||||
&& !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));
|
||||
}
|
||||
// Ice Shroud
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.ice_shroud) &&
|
||||
!MagicDamage.isEntityImmune(DamageType.FROST, event.getEntityLiving()))
|
||||
attacker.addPotionEffect(new PotionEffect(WizardryPotions.frost, 100, 0));
|
||||
|
||||
// Static Aura
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.static_aura)){
|
||||
|
||||
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,
|
||||
@@ -317,44 +238,17 @@ public final class WizardryEventHandler {
|
||||
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){
|
||||
|
||||
@@ -395,212 +289,132 @@ public final class WizardryEventHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@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<EntityWizard> 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();
|
||||
EntityPlayer player = (EntityPlayer)event.getEntityLiving();
|
||||
|
||||
if(WizardData.get(entityplayer) != null){
|
||||
WizardData.get(entityplayer).update();
|
||||
if(player.worldObj.isRemote) hackilyFixContinuousSpellCasting(player);
|
||||
|
||||
if(player.openContainer instanceof ContainerWorkbench){
|
||||
craftingTableTick(player);
|
||||
}
|
||||
|
||||
if(entityplayer.openContainer instanceof ContainerWorkbench){
|
||||
craftingTableTick(entityplayer);
|
||||
}
|
||||
|
||||
if(entityplayer.openContainer instanceof ContainerPlayer){
|
||||
if(player.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);
|
||||
playerInventoryTick(player);
|
||||
}
|
||||
|
||||
testForArmourSet:{
|
||||
for(ItemStack stack : entityplayer.getArmorInventoryList()){
|
||||
for(ItemStack stack : player.getArmorInventoryList()){
|
||||
if(stack == null || !(stack.getItem() instanceof ItemWizardArmour)){
|
||||
break testForArmourSet;
|
||||
}
|
||||
}
|
||||
entityplayer.addStat(WizardryAchievements.armour_set);
|
||||
player.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();
|
||||
SpellModifiers modifiers = ((ISpellCaster)event.getEntity()).getModifiers();
|
||||
|
||||
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<Entity> 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(!MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(event.getEntityLiving(), spell, modifiers,
|
||||
SpellCastEvent.Source.NPC, 0))){
|
||||
|
||||
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(), modifiers);
|
||||
}
|
||||
}
|
||||
// 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)));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void playerInventoryTick(EntityPlayer player) {
|
||||
@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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onItemPickupEvent(EntityItemPickupEvent event){
|
||||
if(event.getItem().getEntityItem().getItem() == WizardryItems.magic_crystal){
|
||||
event.getEntityPlayer().addStat(WizardryAchievements.crystal, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Private helper methods
|
||||
// ================================================================================================================
|
||||
|
||||
/**
|
||||
* Detects inconsistencies between player.getActiveItemStack and the actual itemstack and forces them to be equal.
|
||||
* Fixes issue #25.
|
||||
* @param player
|
||||
*/
|
||||
private static void hackilyFixContinuousSpellCasting(EntityPlayer player){
|
||||
if(player.isHandActive() && player.getHeldItem(player.getActiveHand()) != null
|
||||
&& player.getHeldItem(player.getActiveHand()).getItem() instanceof ItemWand
|
||||
&& WandHelper.getCurrentSpell(player.getHeldItem(player.getActiveHand())).isContinuous){
|
||||
if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())){
|
||||
player.setHeldItem(player.getActiveHand(), player.getActiveItemStack());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -650,8 +464,7 @@ public final class WizardryEventHandler {
|
||||
|
||||
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.
|
||||
// Charges wand using mana flask
|
||||
|
||||
boolean flag = false;
|
||||
ItemStack wand = null;
|
||||
@@ -696,243 +509,4 @@ public final class WizardryEventHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@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<Enchantment, Integer> 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<Enchantment, Integer> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,18 +3,24 @@ package electroblob.wizardry.block;
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
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.init.Blocks;
|
||||
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;
|
||||
import net.minecraftforge.event.entity.player.BonemealEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
// Extending BlockBush allows me to remove nearly everything from this class.
|
||||
@Mod.EventBusSubscriber
|
||||
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);
|
||||
@@ -43,4 +49,20 @@ public class BlockCrystalFlower extends BlockBush {
|
||||
public EnumPlantType getPlantType(IBlockAccess world, BlockPos pos) {
|
||||
return EnumPlantType.Plains;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package electroblob.wizardry.block;
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.tileentity.TileEntityTimer;
|
||||
import electroblob.wizardry.util.WizardryParticleType;
|
||||
import net.minecraft.block.Block;
|
||||
@@ -17,11 +18,15 @@ import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
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;
|
||||
|
||||
// For future reference - extend BlockContainer whenever possible because it has methods for removing tile entities on
|
||||
// block break.
|
||||
@Mod.EventBusSubscriber
|
||||
public class BlockSpectral extends BlockContainer {
|
||||
|
||||
public BlockSpectral(Material material) {
|
||||
@@ -90,5 +95,13 @@ public class BlockSpectral extends BlockContainer {
|
||||
|
||||
return block == this ? false : super.shouldSideBeRendered(blockState, blockAccess, pos, side);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onBlockPlaceEvent(BlockEvent.PlaceEvent event){
|
||||
// Spectral blocks cannot be built on
|
||||
if(event.getPlacedAgainst() == WizardryBlocks.spectral_block){
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -97,6 +97,8 @@ 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.event.SpellCastEvent;
|
||||
import electroblob.wizardry.event.SpellCastEvent.Source;
|
||||
import electroblob.wizardry.item.ItemScroll;
|
||||
import electroblob.wizardry.item.ItemSpellBook;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
@@ -112,7 +114,6 @@ 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;
|
||||
@@ -128,6 +129,7 @@ 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.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
@@ -338,14 +340,23 @@ public class ClientProxy extends CommonProxy {
|
||||
|
||||
// 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);
|
||||
spell.cast(world, (EntityPlayer)caster, message.hand, 0, message.modifiers);
|
||||
|
||||
Source source = Source.OTHER;
|
||||
|
||||
if(((EntityPlayer)caster).getHeldItem(message.hand) != null){
|
||||
Item item = ((EntityPlayer)caster).getHeldItem(message.hand).getItem();
|
||||
if(item instanceof ItemWand){
|
||||
source = Source.WAND;
|
||||
}else if(item instanceof ItemScroll){
|
||||
source = Source.SCROLL;
|
||||
}
|
||||
}
|
||||
|
||||
// No need to check if the spell succeeded, because the packet is only ever sent when it succeeds.
|
||||
// The handler for this event now deals with discovery.
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post((EntityPlayer)caster, spell, message.modifiers, source));
|
||||
|
||||
}else{
|
||||
Wizardry.logger.warn("Recieved a PacketCastSpell, but the caster ID was not the ID of a player");
|
||||
}
|
||||
@@ -385,7 +396,10 @@ public class ClientProxy extends CommonProxy {
|
||||
if(caster instanceof EntityLiving){
|
||||
|
||||
if(target instanceof EntityLivingBase){
|
||||
|
||||
spell.cast(world, (EntityLiving)caster, message.hand, 0, (EntityLivingBase)target, message.modifiers);
|
||||
// Again, no need to check if the spell succeeded, because the packet is only ever sent when it succeeds.
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post((EntityLiving)caster, spell, message.modifiers, Source.NPC));
|
||||
}
|
||||
|
||||
if(caster instanceof ISpellCaster){
|
||||
|
||||
@@ -4,6 +4,8 @@ import java.util.List;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.event.SpellCastEvent.Source;
|
||||
import electroblob.wizardry.packet.PacketCastSpell;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
@@ -24,6 +26,7 @@ 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.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
|
||||
public class CommandCastSpell extends CommandBase {
|
||||
@@ -63,12 +66,14 @@ public class CommandCastSpell extends CommandBase {
|
||||
throw new WrongUsageException("commands.wizardry:cast.usage", Wizardry.settings.castCommandName);
|
||||
}else{
|
||||
|
||||
// ===== Parameter retrieval =====
|
||||
|
||||
int i=0;
|
||||
|
||||
EntityPlayerMP entityplayermp = null;
|
||||
EntityPlayerMP caster = null;
|
||||
|
||||
try{
|
||||
entityplayermp = getCommandSenderAsPlayer(sender);
|
||||
caster = 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.
|
||||
}
|
||||
@@ -85,10 +90,10 @@ public class CommandCastSpell extends CommandBase {
|
||||
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){
|
||||
EntityPlayerMP entityplayermp = getPlayer(server, sender, arguments[i++]);
|
||||
if(caster != entityplayermp){
|
||||
castAsOtherPlayer = true;
|
||||
entityplayermp = entityplayermp1;
|
||||
caster = entityplayermp;
|
||||
}
|
||||
}catch(PlayerNotFoundException exception){
|
||||
// If no player was found, rather than give an error it simply assumes the player was unspecified.
|
||||
@@ -98,7 +103,7 @@ public class CommandCastSpell extends CommandBase {
|
||||
|
||||
// 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.");
|
||||
if(caster == null) throw new PlayerNotFoundException("You must specify which player you wish to perform this action on.");
|
||||
|
||||
SpellModifiers modifiers = new SpellModifiers();
|
||||
|
||||
@@ -122,20 +127,30 @@ public class CommandCastSpell extends CommandBase {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ===== Spell casting =====
|
||||
|
||||
// If anything stops the spell working at this point, nothing else happens.
|
||||
if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Pre(caster, spell, modifiers, Source.COMMAND))){
|
||||
displayFailMessage(sender, spell);
|
||||
return;
|
||||
}
|
||||
|
||||
WizardData data = WizardData.get((EntityPlayer)caster);
|
||||
|
||||
if(spell.isContinuous){
|
||||
|
||||
WizardData properties = WizardData.get((EntityPlayer)entityplayermp);
|
||||
// Events for continuous spell casting via commands are dealt with in WizardData.
|
||||
|
||||
if(properties != null){
|
||||
if(properties.isCasting()){
|
||||
WizardData.get((EntityPlayer)entityplayermp).stopCastingContinuousSpell();
|
||||
if(data != null){
|
||||
if(data.isCasting()){
|
||||
data.stopCastingContinuousSpell();
|
||||
}else{
|
||||
|
||||
WizardData.get((EntityPlayer)entityplayermp).startCastingContinuousSpell(spell, modifiers);
|
||||
data.startCastingContinuousSpell(spell, modifiers);
|
||||
|
||||
if(castAsOtherPlayer){
|
||||
sender.addChatMessage(new TextComponentTranslation("commands.wizardry:cast.success_remote_continuous", spell.getNameForTranslationFormatted(), entityplayermp.getName()));
|
||||
sender.addChatMessage(new TextComponentTranslation("commands.wizardry:cast.success_remote_continuous", spell.getNameForTranslationFormatted(), caster.getName()));
|
||||
}else{
|
||||
sender.addChatMessage(new TextComponentTranslation("commands.wizardry:cast.success_continuous", spell.getNameForTranslationFormatted()));
|
||||
}
|
||||
@@ -146,27 +161,20 @@ public class CommandCastSpell extends CommandBase {
|
||||
|
||||
}else{
|
||||
|
||||
if(spell.cast(entityplayermp.worldObj, entityplayermp, EnumHand.MAIN_HAND, 0, modifiers)){
|
||||
if(spell.cast(caster.worldObj, caster, EnumHand.MAIN_HAND, 0, modifiers)){
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(caster, spell, modifiers, Source.COMMAND));
|
||||
|
||||
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());
|
||||
IMessage msg = new PacketCastSpell.Message(caster.getEntityId(), null, spell.id(), modifiers);
|
||||
WizardryPacketHandler.net.sendToDimension(msg, caster.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()));
|
||||
sender.addChatMessage(new TextComponentTranslation("commands.wizardry:cast.success_remote", spell.getNameForTranslationFormatted(), caster.getName()));
|
||||
}else{
|
||||
sender.addChatMessage(new TextComponentTranslation("commands.wizardry:cast.success", spell.getNameForTranslationFormatted()));
|
||||
}
|
||||
@@ -174,10 +182,15 @@ public class CommandCastSpell extends CommandBase {
|
||||
}
|
||||
}
|
||||
|
||||
ITextComponent message = new TextComponentTranslation("commands.wizardry:cast.fail", spell.getNameForTranslationFormatted());
|
||||
message.getStyle().setColor(TextFormatting.RED);
|
||||
sender.addChatMessage(message);
|
||||
displayFailMessage(sender, spell);
|
||||
}
|
||||
}
|
||||
|
||||
/** Displays the "Unable to cast [spell]" message in the chat. */
|
||||
private void displayFailMessage(ICommandSender sender, Spell spell){
|
||||
ITextComponent message = new TextComponentTranslation("commands.wizardry:cast.fail", spell.getNameForTranslationFormatted());
|
||||
message.getStyle().setColor(TextFormatting.RED);
|
||||
sender.addChatMessage(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.util.List;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.event.DiscoverSpellEvent;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import net.minecraft.command.CommandBase;
|
||||
@@ -16,6 +17,7 @@ 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.minecraftforge.common.MinecraftForge;
|
||||
|
||||
public class CommandDiscoverSpell extends CommandBase {
|
||||
|
||||
@@ -66,10 +68,10 @@ public class CommandDiscoverSpell extends CommandBase {
|
||||
boolean clear = false;
|
||||
boolean all = false;
|
||||
|
||||
EntityPlayerMP entityplayermp = null;
|
||||
EntityPlayerMP player = null;
|
||||
|
||||
try{
|
||||
entityplayermp = getCommandSenderAsPlayer(sender);
|
||||
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.
|
||||
}
|
||||
@@ -94,32 +96,34 @@ public class CommandDiscoverSpell extends CommandBase {
|
||||
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;
|
||||
EntityPlayerMP entityplayermp = getPlayer(server, sender, arguments[i++]);
|
||||
if(player != entityplayermp){
|
||||
player = entityplayermp;
|
||||
}
|
||||
}
|
||||
|
||||
// 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.");
|
||||
if(player == null) throw new PlayerNotFoundException("You must specify which player you wish to perform this action on.");
|
||||
|
||||
WizardData properties = WizardData.get(entityplayermp);
|
||||
WizardData properties = WizardData.get(player);
|
||||
|
||||
if(properties != null){
|
||||
if(clear){
|
||||
properties.spellsDiscovered.clear();
|
||||
sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.clear", entityplayermp.getName()));
|
||||
sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.clear", player.getName()));
|
||||
}else if(all){
|
||||
properties.spellsDiscovered.addAll(Spell.getSpells(Spell.allSpells));
|
||||
sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.all", entityplayermp.getName()));
|
||||
sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.all", player.getName()));
|
||||
}else{
|
||||
if(properties.hasSpellBeenDiscovered(spell)){
|
||||
properties.spellsDiscovered.remove(spell);
|
||||
sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.removespell", spell.getNameForTranslationFormatted(), entityplayermp.getName()));
|
||||
sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.removespell", spell.getNameForTranslationFormatted(), player.getName()));
|
||||
}else{
|
||||
properties.discoverSpell(spell);
|
||||
sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.addspell", spell.getNameForTranslationFormatted(), entityplayermp.getName()));
|
||||
if(!MinecraftForge.EVENT_BUS.post(new DiscoverSpellEvent(player, spell, DiscoverSpellEvent.Source.COMMAND))){
|
||||
properties.discoverSpell(spell);
|
||||
sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.addspell", spell.getNameForTranslationFormatted(), player.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
properties.sync();
|
||||
|
||||
@@ -1,11 +1,131 @@
|
||||
package electroblob.wizardry.enchantment;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardryEnchantments;
|
||||
import electroblob.wizardry.spell.FreezingWeapon;
|
||||
import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.projectile.EntityArrow;
|
||||
import net.minecraft.inventory.ContainerChest;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.ItemBow;
|
||||
import net.minecraft.item.ItemEnchantedBook;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
||||
import net.minecraftforge.event.entity.item.ItemTossEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingDropsEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
/** 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 */
|
||||
@Mod.EventBusSubscriber
|
||||
public interface Imbuement {
|
||||
|
||||
// This interface has no abstract methods, these handlers are just here for the sake of keeping things organised.
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingDropsEvent(LivingDropsEvent event){
|
||||
// Instantly disenchants an imbued weapon if it is dropped when the player dies.
|
||||
for(EntityItem item : event.getDrops()){
|
||||
removeImbuements(item.getEntityItem());
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onItemTossEvent(ItemTossEvent event){
|
||||
// Instantly disenchants an imbued weapon if it is thrown on the ground.
|
||||
removeImbuements(event.getEntityItem().getEntityItem());
|
||||
}
|
||||
|
||||
/** Removes all imbuements from the given itemstack. */
|
||||
static void removeImbuements(ItemStack stack){
|
||||
if(stack.isItemEnchanted()){
|
||||
// No need to check what enchantments the item has, since remove() does nothing if the element does not exist.
|
||||
Map<Enchantment, Integer> enchantments = EnchantmentHelper.getEnchantments(stack);
|
||||
// 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, stack);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerOpenContainerEvent(PlayerContainerEvent event){
|
||||
// Brute-force fix to stop enchanted books in dungeon chests from having imbuements on them.
|
||||
if(event.getContainer() instanceof ContainerChest){
|
||||
// Still not sure if it's better to set stacks in slots or modify the itemstack list directly, but I would
|
||||
// imagine it's the former.
|
||||
for(Slot slot : event.getContainer().inventorySlots){
|
||||
if(slot.getStack() != null && slot.getStack().getItem() instanceof ItemEnchantedBook){
|
||||
// We don't care about the level of the enchantments
|
||||
Map<Enchantment, Integer> enchantments = EnchantmentHelper.getEnchantments(slot.getStack());
|
||||
// Removes all imbuements
|
||||
if(enchantments.keySet().removeIf(e -> e instanceof Imbuement)){
|
||||
// If any imbuements were removed, replaces the enchantments on the book with the new ones, or
|
||||
// deletes the book entirely if there are none left.
|
||||
if(enchantments.isEmpty()){
|
||||
slot.putStack(null); // NOTE: Will need changing in 1.11
|
||||
Wizardry.logger.info("Deleted enchanted book with illegal enchantments");
|
||||
}else{
|
||||
EnchantmentHelper.setEnchantments(enchantments, slot.getStack());
|
||||
Wizardry.logger.info("Removed illegal enchantments from enchanted book");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onEntityJoinWorld(EntityJoinWorldEvent event){
|
||||
// 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();
|
||||
|
||||
if(arrow.shootingEntity instanceof EntityLivingBase){
|
||||
|
||||
EntityLivingBase archer = (EntityLivingBase)arrow.shootingEntity;
|
||||
|
||||
ItemStack bow = archer.getHeldItemMainhand();
|
||||
|
||||
if(bow == null || !(bow.getItem() instanceof ItemBow)){
|
||||
bow = archer.getHeldItemOffhand();
|
||||
if(bow == null || !(bow.getItem() instanceof ItemBow)) return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,11 @@ import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class EntityBubble extends EntityMagicConstruct {
|
||||
|
||||
public boolean isDarkOrb;
|
||||
@@ -119,5 +123,15 @@ public class EntityBubble extends EntityMagicConstruct {
|
||||
super.readSpawnData(data);
|
||||
this.isDarkOrb = data.readBoolean();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingAttackEvent(LivingAttackEvent event){
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,16 +3,18 @@ package electroblob.wizardry.entity.living;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.event.SpellCastEvent.Source;
|
||||
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.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.common.network.NetworkRegistry.TargetPoint;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
|
||||
@@ -20,192 +22,217 @@ import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
/** 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;
|
||||
|
||||
@Override
|
||||
public boolean shouldExecute(){
|
||||
|
||||
EntityLivingBase entitylivingbase = this.attacker.getAttackTarget();
|
||||
/**
|
||||
* 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){
|
||||
|
||||
if(entitylivingbase == null){
|
||||
return false;
|
||||
}else{
|
||||
this.target = entitylivingbase;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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 continueExecuting(){
|
||||
return this.shouldExecute() || !this.attacker.getNavigator().noPath();
|
||||
}
|
||||
public boolean shouldExecute(){
|
||||
|
||||
@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));
|
||||
}
|
||||
EntityLivingBase entitylivingbase = this.attacker.getAttackTarget();
|
||||
|
||||
@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(entitylivingbase == null){
|
||||
return false;
|
||||
}else{
|
||||
this.target = entitylivingbase;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(targetIsVisible){
|
||||
++this.seeTime;
|
||||
}else{
|
||||
this.seeTime = 0;
|
||||
}
|
||||
@Override
|
||||
public boolean continueExecuting(){
|
||||
return this.shouldExecute() || !this.attacker.getNavigator().noPath();
|
||||
}
|
||||
|
||||
if(distanceSq <= (double)this.maxAttackDistance && this.seeTime >= 20){
|
||||
this.attacker.getNavigator().clearPathEntity();
|
||||
}else{
|
||||
this.attacker.getNavigator().tryMoveToEntityLiving(this.target, this.speed);
|
||||
}
|
||||
@Override
|
||||
public void resetTask(){
|
||||
this.target = null;
|
||||
this.seeTime = 0;
|
||||
this.cooldown = -1;
|
||||
this.setContinuousSpellAndNotify(Spells.none, new SpellModifiers());
|
||||
this.continuousSpellTimer = 0;
|
||||
}
|
||||
|
||||
this.attacker.getLookHelper().setLookPositionWithEntity(this.target, 30.0F, 30.0F);
|
||||
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));
|
||||
}
|
||||
|
||||
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;
|
||||
@Override
|
||||
public void updateTask(){
|
||||
|
||||
List<Spell> spells = new ArrayList<Spell>(caster.getSpells());
|
||||
|
||||
if(spells.size() > 0){
|
||||
// Only executed server side.
|
||||
|
||||
if(!attacker.worldObj.isRemote){
|
||||
double distanceSq = this.attacker.getDistanceSq(this.target.posX, this.target.getEntityBoundingBox().minY, this.target.posZ);
|
||||
boolean targetIsVisible = this.attacker.getEntitySenses().canSee(this.target);
|
||||
|
||||
// New way of choosing a spell; keeps trying until one works or all have been tried
|
||||
if(targetIsVisible){
|
||||
++this.seeTime;
|
||||
}else{
|
||||
this.seeTime = 0;
|
||||
}
|
||||
|
||||
Spell spell;
|
||||
if(distanceSq <= (double)this.maxAttackDistance && this.seeTime >= 20){
|
||||
this.attacker.getNavigator().clearPathEntity();
|
||||
}else{
|
||||
this.attacker.getNavigator().tryMoveToEntityLiving(this.target, this.speed);
|
||||
}
|
||||
|
||||
while(!spells.isEmpty()){
|
||||
this.attacker.getLookHelper().setLookPositionWithEntity(this.target, 30.0F, 30.0F);
|
||||
|
||||
spell = spells.get(attacker.worldObj.rand.nextInt(spells.size()));
|
||||
|
||||
SpellModifiers modifiers = caster.getModifiers();
|
||||
if(this.continuousSpellTimer > 0){
|
||||
|
||||
if(spell != null && spell.cast(attacker.worldObj, attacker, EnumHand.MAIN_HAND, 0, target, modifiers)){
|
||||
this.continuousSpellTimer--;
|
||||
|
||||
// If the target goes out of range or out of sight...
|
||||
if(distanceSq > (double)this.maxAttackDistance || !targetIsVisible
|
||||
// ...or the spell is cancelled via events...
|
||||
|| MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(attacker, caster.getContinuousSpell(),
|
||||
caster.getModifiers(), Source.NPC, this.continuousSpellDuration - this.continuousSpellTimer))
|
||||
// ...or the spell no longer succeeds...
|
||||
|| !caster.getContinuousSpell().cast(attacker.worldObj, attacker, EnumHand.MAIN_HAND,
|
||||
this.continuousSpellDuration - this.continuousSpellTimer, target, caster.getModifiers())
|
||||
// ...or the time has elapsed...
|
||||
|| this.continuousSpellTimer == 0){
|
||||
|
||||
// ...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.continuousSpellDuration - this.continuousSpellTimer == 1){
|
||||
// On the first tick, if the spell did succeed, fire SpellCastEvent.Post.
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(attacker, caster.getContinuousSpell(),
|
||||
caster.getModifiers(), Source.NPC));
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}else if(--this.cooldown == 0){
|
||||
|
||||
attacker.rotationYaw = (float)(Math.atan2(dz, dx) * 180.0D / Math.PI) - 90.0F;
|
||||
if(distanceSq > (double)this.maxAttackDistance || !targetIsVisible){
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
double dx = target.posX - attacker.posX;
|
||||
double dz = target.posZ - attacker.posZ;
|
||||
|
||||
}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;
|
||||
}
|
||||
}
|
||||
List<Spell> spells = new ArrayList<Spell>(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 && attemptCastSpell(spell, modifiers)){
|
||||
// The spell worked, so we're done!
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/** Attempts to cast the given spell (including event firing) and returns true if it succeeded. */
|
||||
private boolean attemptCastSpell(Spell spell, SpellModifiers modifiers){
|
||||
|
||||
// If anything stops the spell working at this point, nothing else happens.
|
||||
if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Pre(attacker, spell, modifiers, Source.NPC))){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(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{
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(attacker, spell, modifiers, Source.NPC));
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
|
||||
protected Predicate<Entity> targetSelector;
|
||||
|
||||
/** Data parameter for the cooldown time for wizards healing themselves. */
|
||||
private static final DataParameter<Integer> HEAL_COOLDOWN = EntityDataManager.createKey(EntityWizard.class, DataSerializers.VARINT);
|
||||
private static final DataParameter<Integer> HEAL_COOLDOWN = EntityDataManager.createKey(EntityEvilWizard.class, DataSerializers.VARINT);
|
||||
/** Data parameter for the wizard's element. */
|
||||
private static final DataParameter<Integer> ELEMENT = EntityDataManager.createKey(EntityEvilWizard.class, DataSerializers.VARINT);
|
||||
/** The resource location for the evil wizard's loot table. */
|
||||
|
||||
@@ -278,6 +278,7 @@ public class EntityIceWraith extends EntityBlazeMinion {
|
||||
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());
|
||||
// TODO: Decide if an event should be fired here. I'm guessing no.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -162,6 +162,7 @@ public class EntityLightningWraith extends EntityBlazeMinion {
|
||||
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());
|
||||
// TODO: Decide if an event should be fired here. I'm guessing no.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,13 +69,18 @@ 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.FakePlayer;
|
||||
import net.minecraftforge.common.util.Constants.NBT;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
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;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class EntityWizard extends EntityVillager implements ISpellCaster, IEntityAdditionalSpawnData {
|
||||
|
||||
/*
|
||||
@@ -779,6 +784,25 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
|
||||
// Uses .equals() rather than == so this will work fine.
|
||||
return this.towerBlocks.contains(pos);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onBlockBreakEvent(BlockEvent.BreakEvent event){
|
||||
// Makes wizards angry if a player breaks a block in their tower
|
||||
if(!(event.getPlayer() instanceof FakePlayer)){
|
||||
|
||||
List<EntityWizard> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EntityVillager overrides (that don't add features)
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.WizardryEventHandler;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.util.IElementalDamage;
|
||||
import electroblob.wizardry.util.IndirectMinionDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.MinionDamage;
|
||||
import electroblob.wizardry.util.WizardryParticleType;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
@@ -23,10 +27,16 @@ 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.DamageSource;
|
||||
import net.minecraft.util.EntityDamageSource;
|
||||
import net.minecraft.util.EntityDamageSourceIndirect;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
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 for all summoned creatures. The code for summoned creatures has been overhauled in Wizardry 2.1, and this
|
||||
* interface allows summoned creatures to extend vanilla (or indeed modded) entity classes, so <code>EntitySummonedZombie</code>
|
||||
* now extends <code>EntityZombie</code>, for example. This change has two major benefits:
|
||||
* <p>
|
||||
@@ -59,13 +69,14 @@ import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
|
||||
* <p>
|
||||
* 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'. <b>Don't call them, only implement them.</b>
|
||||
* @since Wizardry 1.2
|
||||
* @since Wizardry 2.1
|
||||
* @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!
|
||||
*/
|
||||
@Mod.EventBusSubscriber
|
||||
public interface ISummonedCreature extends IEntityAdditionalSpawnData {
|
||||
|
||||
// Remember that ALL fields are static and final in interfaces, even if they don't explicitly state that.
|
||||
@@ -78,7 +89,7 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
|
||||
|
||||
/** 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
|
||||
* is done automatically. As of Wizardry 2.1, despawning is handled in ISummonedCreature; see
|
||||
* {@link ISummonedCreature#onDespawn()} for details. */
|
||||
int getLifetime();
|
||||
|
||||
@@ -274,5 +285,49 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Damage system
|
||||
|
||||
@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 this fixes that.
|
||||
if(WizardryUtilities.attackEntityWithoutKnockback(event.getEntity(), newSource, event.getAmount())){
|
||||
WizardryUtilities.applyStandardKnockback(event.getSource().getEntity(), event.getEntityLiving());
|
||||
((ISummonedCreature)event.getSource().getEntity()).onSuccessfulAttack(event.getEntityLiving());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,10 +7,9 @@ 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.<br>
|
||||
* DiscoverSpellEvent is fired when a player discovers a spell by any method.<br>
|
||||
* <br>
|
||||
* This event is {@link Cancelable}.<br>
|
||||
* If this event is canceled, the spell is not discovered.<br>
|
||||
* This event is {@link Cancelable}. If this event is canceled, the spell is not discovered.<br>
|
||||
* <br>
|
||||
* This event does not have a result. {@link HasResult}<br>
|
||||
* <br>
|
||||
@@ -21,15 +20,30 @@ import net.minecraftforge.fml.common.eventhandler.Cancelable;
|
||||
*/
|
||||
public class DiscoverSpellEvent extends PlayerEvent {
|
||||
|
||||
private final Spell spell;
|
||||
private final Source source;
|
||||
|
||||
public DiscoverSpellEvent(EntityPlayer player, Spell spell, Source source) {
|
||||
super(player);
|
||||
this.spell = spell;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/** Returns the spell that is being discovered. */
|
||||
public Spell getSpell(){
|
||||
return spell;
|
||||
}
|
||||
|
||||
/** Returns the method used to discover the spell. */
|
||||
public Source getSource(){
|
||||
return source;
|
||||
}
|
||||
|
||||
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
|
||||
/** Signifies that the spell was discovered by some other means. */ OTHER
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
package electroblob.wizardry.event;
|
||||
|
||||
import electroblob.wizardry.tileentity.ContainerArcaneWorkbench;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.Cancelable;
|
||||
import net.minecraftforge.fml.common.eventhandler.Event.HasResult;
|
||||
|
||||
/**
|
||||
* [NYI] SpellBindEvent is fired when a player presses the apply button in the arcane workbench.<br>
|
||||
* SpellBindEvent is fired when a player presses the apply button in the arcane workbench. <i>Note that this
|
||||
* event is only fired on the server side.</i><br>
|
||||
* <br>
|
||||
* This event is {@link Cancelable}.<br>
|
||||
* If this event is canceled, no further processing takes place: spells are not bound, upgrades are not applied and
|
||||
* crystals are not consumed.<br>
|
||||
* 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.<br>
|
||||
* <br>
|
||||
* This event does not have a result. {@link HasResult}<br>
|
||||
* <br>
|
||||
@@ -19,10 +21,11 @@ import net.minecraftforge.fml.common.eventhandler.Cancelable;
|
||||
* @author Electroblob
|
||||
* @since Wizardry 2.1
|
||||
*/
|
||||
public class SpellBindEvent extends PlayerEvent {
|
||||
@Cancelable
|
||||
public class SpellBindEvent extends PlayerContainerEvent {
|
||||
|
||||
public SpellBindEvent(EntityPlayer player) {
|
||||
super(player);
|
||||
public SpellBindEvent(EntityPlayer player, ContainerArcaneWorkbench container) {
|
||||
super(player, container);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
package electroblob.wizardry.event;
|
||||
|
||||
import electroblob.wizardry.entity.living.ISpellCaster;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.Cancelable;
|
||||
|
||||
/**
|
||||
* [NYI] SpellCastEvent is the parent event class for all spell casting events.<br>
|
||||
* SpellCastEvent is the parent event class for all spell casting events. Methods which subscribe to this event
|
||||
* will receive all three child events (it is recommended that you use {@link SpellCastEvent.Pre},
|
||||
* {@link SpellCastEvent.Post} or {@link SpellCastEvent.Tick}, depending on the application).<br>
|
||||
* <br>
|
||||
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.
|
||||
*
|
||||
@@ -14,9 +19,121 @@ import net.minecraftforge.event.entity.living.LivingEvent;
|
||||
* @since Wizardry 2.1
|
||||
*/
|
||||
public abstract class SpellCastEvent extends LivingEvent {
|
||||
|
||||
private final Spell spell;
|
||||
private final SpellModifiers modifiers;
|
||||
private final Source source;
|
||||
|
||||
public SpellCastEvent(EntityLivingBase entity, Spell spell) {
|
||||
super(entity);
|
||||
public SpellCastEvent(EntityLivingBase caster, Spell spell, SpellModifiers modifiers, Source source) {
|
||||
super(caster);
|
||||
this.spell = spell;
|
||||
this.modifiers = modifiers;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/** Returns the spell being cast. */
|
||||
public Spell getSpell(){
|
||||
return spell;
|
||||
}
|
||||
|
||||
/** Returns the modifiers for the spell being cast. */
|
||||
public SpellModifiers getModifiers(){
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
/** Returns the source of the spell being cast. */
|
||||
public Source getSource(){
|
||||
return source;
|
||||
}
|
||||
|
||||
public enum Source {
|
||||
/** Signifies that the spell was cast using a wand. */ WAND,
|
||||
/** Signifies that the spell was cast using a scroll. */ SCROLL,
|
||||
/** Signifies that the spell was cast using commands. */ COMMAND,
|
||||
/** Signifies that the spell was cast by an {@link ISpellCaster}. */ NPC,
|
||||
/** Signifies that the spell was cast by some other means. */ OTHER
|
||||
}
|
||||
|
||||
/**
|
||||
* SpellCastEvent.Pre is fired just before a spell is cast. Use this event to change the spell modifiers and
|
||||
* generally alter the behaviour of the spell, or stop it from being cast entirely. For example, wizardry uses this
|
||||
* event to cancel spells cast by entities that have the arcane jammer effect. Note that for wands, this is called
|
||||
* <i>before</i> mana, tier and cooldowns are checked. Also note that this event is only fired once for continuous
|
||||
* spells, when they start casting.<br>
|
||||
* <br>
|
||||
* This event is {@link Cancelable}. If this event is canceled, the spell is not cast, mana is not consumed, and the
|
||||
* right-click action that caused it (if any) returns a result of FAIL, meaning that the right-click is passed to
|
||||
* the block/entity in front of the player, if any.<br>
|
||||
* <br>
|
||||
* This event does not have a result. {@link HasResult}<br>
|
||||
* <br>
|
||||
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 2.1
|
||||
*/
|
||||
@Cancelable
|
||||
public static class Pre extends SpellCastEvent {
|
||||
|
||||
public Pre(EntityLivingBase caster, Spell spell, SpellModifiers modifiers, Source source) {
|
||||
super(caster, spell, modifiers, source);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* SpellCastEvent.Post is fired just after a spell is cast. Use this event for any processing which depends
|
||||
* on whether the spell succeeds, and does not affect the spell itself. For example, wizardry uses this event
|
||||
* to keep track of spellcasting stats. Note that although this event is fired from both sides, it is not fired from
|
||||
* common code; rather, both sides fire it separately, so timing is not guaranteed. Also note that this event is
|
||||
* only fired once for continuous spells, after the first casting tick.<br>
|
||||
* <br>
|
||||
* This event is not {@link Cancelable}.<br>
|
||||
* <br>
|
||||
* This event does not have a result. {@link HasResult}<br>
|
||||
* <br>
|
||||
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 2.1
|
||||
*/
|
||||
public static class Post extends SpellCastEvent {
|
||||
|
||||
public Post(EntityLivingBase caster, Spell spell, SpellModifiers modifiers, Source source) {
|
||||
super(caster, spell, modifiers, source);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* SpellCastEvent.Tick is fired each tick while a continuous spell is being cast.<br>
|
||||
* <br>
|
||||
* This event is {@link Cancelable}. If this event is canceled, the spell is not cast, mana is not consumed, and the
|
||||
* spell casting is interrupted. Cancelling this event on the client side will stop particles being spawned, but
|
||||
* will not interrupt spell casting.<br>
|
||||
* <br>
|
||||
* This event does not have a result. {@link HasResult}<br>
|
||||
* <br>
|
||||
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 2.1
|
||||
*/
|
||||
@Cancelable
|
||||
public static class Tick extends SpellCastEvent {
|
||||
|
||||
private final int count;
|
||||
|
||||
public Tick(EntityLivingBase caster, Spell spell, SpellModifiers modifiers, Source source, int count) {
|
||||
super(caster, spell, modifiers, source);
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
/** Returns the number of ticks this (continuous) spell has already been cast for. */
|
||||
public int getCount(){
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraftforge.event.entity.item.ItemTossEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingDropsEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
/** 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. */
|
||||
@Mod.EventBusSubscriber
|
||||
public interface IConjuredItem {
|
||||
|
||||
/** The NBT tag key used to store the duration multiplier for conjured items. */
|
||||
@@ -29,4 +35,23 @@ public interface IConjuredItem {
|
||||
/** 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();
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingDropsEvent(LivingDropsEvent event){
|
||||
// Destroys conjured items if their caster dies.
|
||||
for(EntityItem item : event.getDrops()){
|
||||
if(item.getEntityItem().getItem() instanceof IConjuredItem){
|
||||
item.setDead();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package electroblob.wizardry.item;
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.event.DiscoverSpellEvent;
|
||||
import electroblob.wizardry.registry.WizardryAchievements;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
@@ -17,6 +18,7 @@ import net.minecraft.util.EnumActionResult;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@@ -54,15 +56,17 @@ public class ItemIdentificationScroll extends Item {
|
||||
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()));
|
||||
if(!MinecraftForge.EVENT_BUS.post(new DiscoverSpellEvent(player, spell, DiscoverSpellEvent.Source.IDENTIFICATION_SCROLL))){
|
||||
// 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<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,23 @@ package electroblob.wizardry.item;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.event.SpellCastEvent.Source;
|
||||
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.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
@@ -36,7 +35,6 @@ public class ItemScroll extends Item {
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void getSubItems(Item item, CreativeTabs par2CreativeTabs, List<ItemStack> 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()));
|
||||
}
|
||||
@@ -70,59 +68,42 @@ public class ItemScroll extends Item {
|
||||
@Override
|
||||
public ActionResult<ItemStack> onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){
|
||||
|
||||
if(player.isPotionActive(WizardryPotions.arcane_jammer)) return new ActionResult<ItemStack>(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()));
|
||||
// By default, scrolls have no modifiers - but with the event system, they could be added.
|
||||
SpellModifiers modifiers = new SpellModifiers();
|
||||
|
||||
// If anything stops the spell working at this point, nothing else happens.
|
||||
if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Pre(player, spell, modifiers, Source.SCROLL))){
|
||||
return new ActionResult<ItemStack>(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())){
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.SCROLL));
|
||||
|
||||
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());
|
||||
IMessage msg = new PacketCastSpell.Message(player.getEntityId(), hand, spell.id(), modifiers);
|
||||
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);
|
||||
|
||||
// Scrolls are consumed upon successful use in survival mode
|
||||
if(!player.capabilities.isCreativeMode) stack.stackSize--;
|
||||
|
||||
return new ActionResult<ItemStack>(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<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
// Client-inconsistent spell casting. This code only runs client-side.
|
||||
if(spell.cast(world, player, hand, 0, modifiers)){
|
||||
// This is all that needs to happen, because everything above works fine on just the server side.
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.SCROLL));
|
||||
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.living.ISummonedCreature;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.event.SpellCastEvent.Source;
|
||||
import electroblob.wizardry.packet.PacketCastSpell;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.WizardryAchievements;
|
||||
@@ -24,7 +26,6 @@ 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;
|
||||
@@ -32,10 +33,10 @@ 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.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
@@ -79,12 +80,12 @@ public class ItemWand extends Item {
|
||||
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<ItemStack> parListSubItems){
|
||||
parListSubItems.add(new ItemStack(this, 1));
|
||||
}
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void getSubItems(Item parItem, CreativeTabs parTab, List<ItemStack> parListSubItems){
|
||||
parListSubItems.add(new ItemStack(this, 1));
|
||||
}
|
||||
|
||||
// Max damage is modifiable with upgrades.
|
||||
@Override
|
||||
@@ -95,13 +96,6 @@ public class ItemWand extends Item {
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@@ -124,19 +118,19 @@ public class ItemWand extends Item {
|
||||
((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);
|
||||
}
|
||||
|
||||
@@ -174,33 +168,30 @@ public class ItemWand extends Item {
|
||||
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. */
|
||||
* holding the item (I call this client-inconsistency). 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<ItemStack> onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){
|
||||
|
||||
|
||||
// Alternate right-click function; overrides spell casting.
|
||||
if(this.selectMinionTarget(player, world)) return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
|
||||
if(player.isPotionActive(WizardryPotions.arcane_jammer)) return new ActionResult<ItemStack>(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()));
|
||||
SpellModifiers modifiers = this.calculateModifiers(stack, spell);
|
||||
|
||||
// If anything stops the spell working at this point, nothing else happens.
|
||||
if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Pre(player, spell, modifiers, Source.WAND))){
|
||||
return new ActionResult<ItemStack>(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.
|
||||
// This is here to start the inUse thing, otherwise the onUsingTick method will not fire.
|
||||
if(spell.isContinuous && !player.isHandActive()){
|
||||
player.setActiveHand(hand);
|
||||
// Probably ought to be here. (Does it succeed though?)
|
||||
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
}
|
||||
|
||||
// Conditions for the spell to be attempted. The tier check is a failsafe; it should never be false unless the
|
||||
@@ -211,14 +202,13 @@ public class ItemWand extends Item {
|
||||
// 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)){
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.WAND));
|
||||
|
||||
// = Packets =
|
||||
if(spell.doesSpellRequirePacket()){
|
||||
@@ -230,17 +220,6 @@ public class ItemWand extends Item {
|
||||
}
|
||||
|
||||
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
|
||||
@@ -264,53 +243,42 @@ public class ItemWand extends Item {
|
||||
|
||||
return new ActionResult<ItemStack>(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<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
// Client-inconsistent spell casting. This code only runs client-side.
|
||||
if(spell.cast(world, player, hand, 0, modifiers)){
|
||||
// This is all that needs to happen, because everything above works fine on just the server side.
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.WAND));
|
||||
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new ActionResult<ItemStack>(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);
|
||||
|
||||
SpellModifiers modifiers = this.calculateModifiers(stack, spell);
|
||||
int castingTick = stack.getMaxItemUseDuration() - count;
|
||||
|
||||
if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(player, spell, modifiers, Source.WAND, castingTick))) return;
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
if(spell.cast(player.worldObj, player, player.getActiveHand(), castingTick, modifiers)){
|
||||
|
||||
if(castingTick == 0) MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.WAND));
|
||||
|
||||
// = Mana cost =
|
||||
// Divides the mana cost over a second appropriately; since damage is an integer it cannot
|
||||
// just be divided by 20.
|
||||
@@ -320,57 +288,32 @@ public class ItemWand extends Item {
|
||||
// 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){
|
||||
@@ -385,36 +328,61 @@ public class ItemWand extends Item {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
/** 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);
|
||||
@@ -424,7 +392,7 @@ public class ItemWand extends Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
@@ -74,7 +76,7 @@ public class RandomSpell extends LootFunction {
|
||||
@Override
|
||||
public ItemStack apply(ItemStack stack, Random random, LootContext context){
|
||||
|
||||
if(!(stack.getItem() instanceof ItemSpellBook) || !(stack.getItem() instanceof ItemScroll))
|
||||
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;
|
||||
@@ -96,7 +98,12 @@ public class RandomSpell extends LootFunction {
|
||||
|
||||
// Elements aren't weighted
|
||||
if(elements == null || elements.isEmpty()){
|
||||
element = Element.values()[random.nextInt(Element.values().length)];
|
||||
// Element can only be MAGIC if tier is BASIC
|
||||
if(tier == Tier.BASIC){
|
||||
element = Element.values()[random.nextInt(Element.values().length)];
|
||||
}else{
|
||||
element = ArrayUtils.removeElement(Element.values(), Element.MAGIC)[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()));
|
||||
|
||||
@@ -3,7 +3,6 @@ package electroblob.wizardry.packet;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.packet.PacketControlInput.Message;
|
||||
import electroblob.wizardry.tileentity.ContainerArcaneWorkbench;
|
||||
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
@@ -37,8 +36,7 @@ public class PacketControlInput implements IMessageHandler<Message, IMessage> {
|
||||
|
||||
case APPLY_BUTTON:
|
||||
|
||||
TileEntityArcaneWorkbench tileentity = ((ContainerArcaneWorkbench)player.openContainer).tileEntityArcaneWorkbench;
|
||||
tileentity.onApplyButtonPressed(player);
|
||||
((ContainerArcaneWorkbench)player.openContainer).onApplyButtonPressed(player);
|
||||
break;
|
||||
|
||||
case NEXT_SPELL_KEY:
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package electroblob.wizardry.potion;
|
||||
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
/**
|
||||
* Interface for potion effects that spawn custom particles instead of (or as well as) the vanilla 'swirly' particles.
|
||||
@@ -8,6 +12,7 @@ import net.minecraft.world.World;
|
||||
* @since Wizardry 1.2
|
||||
*/
|
||||
// TODO: Backport.
|
||||
@Mod.EventBusSubscriber
|
||||
public interface ICustomPotionParticles {
|
||||
|
||||
/**
|
||||
@@ -20,4 +25,21 @@ public interface ICustomPotionParticles {
|
||||
*/
|
||||
void spawnCustomParticle(World world, double x, double y, double z);
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
if(event.getEntityLiving().worldObj.isRemote){
|
||||
// Behold the power of interfaces!
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package electroblob.wizardry.potion;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.entity.construct.EntityDecay;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.potion.Potion;
|
||||
@@ -32,8 +36,25 @@ public class PotionDecay extends Potion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performEffect(EntityLivingBase entitylivingbase, int strength) {
|
||||
entitylivingbase.attackEntityFrom(DamageSource.wither, 1);
|
||||
public void performEffect(EntityLivingBase target, int strength) {
|
||||
|
||||
target.attackEntityFrom(DamageSource.wither, 1);
|
||||
|
||||
if(target.onGround && target.ticksExisted % Constants.DECAY_SPREAD_INTERVAL == 0){
|
||||
|
||||
List<Entity> entities = target.worldObj.getEntitiesWithinAABBExcludingEntity(target, target.getEntityBoundingBox());
|
||||
|
||||
boolean flag = true;
|
||||
|
||||
for(Entity entity : entities){
|
||||
if(entity 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.
|
||||
target.worldObj.spawnEntityInWorld(new EntityDecay(target.worldObj, target.posX, target.posY, target.posZ, target));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,6 +2,7 @@ package electroblob.wizardry.potion;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.WizardryParticleType;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
@@ -10,9 +11,13 @@ import net.minecraft.potion.Potion;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent.BreakSpeed;
|
||||
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;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class PotionFrost extends Potion implements ICustomPotionParticles {
|
||||
|
||||
private static final ResourceLocation ICON = new ResourceLocation(Wizardry.MODID, "textures/gui/frost_icon.png");
|
||||
@@ -50,4 +55,12 @@ public class PotionFrost extends Potion implements ICustomPotionParticles {
|
||||
WizardryUtilities.drawTexturedRect(x + 3, y + 3, 0, 0, 18, 18, 18, 18);
|
||||
}
|
||||
|
||||
@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)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@ import net.minecraftforge.fml.common.registry.RegistryBuilder;
|
||||
// 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.
|
||||
// you haven't created the registry before you start calling constructors of classes extending IForgeRegistryEntry.Impl,
|
||||
// and secondly,
|
||||
@ObjectHolder(Wizardry.MODID)
|
||||
@Mod.EventBusSubscriber
|
||||
public final class Spells {
|
||||
|
||||
@@ -5,6 +5,7 @@ import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.living.EntityWizard;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.registry.WizardryAchievements;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
@@ -21,7 +22,10 @@ import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class ArcaneJammer extends Spell {
|
||||
|
||||
public ArcaneJammer() {
|
||||
@@ -91,5 +95,11 @@ public class ArcaneJammer extends Spell {
|
||||
public boolean canBeCastByNPCs(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onSpellCastPreEvent(SpellCastEvent.Pre event){
|
||||
// Arcane jammer prevents spell casting.
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.arcane_jammer)) event.setCanceled(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,11 +5,14 @@ import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.packet.PacketClairvoyance;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.util.WizardryParticleType;
|
||||
import electroblob.wizardry.util.WizardryPathFinder;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
@@ -18,6 +21,7 @@ import net.minecraft.entity.monster.EntityZombie;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.pathfinding.Path;
|
||||
import net.minecraft.pathfinding.PathNodeType;
|
||||
import net.minecraft.pathfinding.PathPoint;
|
||||
@@ -25,7 +29,11 @@ import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class Clairvoyance extends Spell {
|
||||
|
||||
/** The number of ticks it takes each path particle to move from one path point to the next. */
|
||||
@@ -123,5 +131,33 @@ public class Clairvoyance extends Spell {
|
||||
|
||||
Wizardry.proxy.spawnParticle(WizardryParticleType.PATH, world, point.xCoord + 0.5, point.yCoord + 0.5, point.zCoord + 0.5, 0, 0, 0, (int)(1800*durationMultiplier), 1, 1, 1);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.IElementalDamage;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryParticleType;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
@@ -17,7 +18,11 @@ import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingHurtEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class CurseOfSoulbinding extends Spell {
|
||||
|
||||
public CurseOfSoulbinding() {
|
||||
@@ -53,5 +58,17 @@ public class CurseOfSoulbinding extends Spell {
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F);
|
||||
return true;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingHurtEvent(LivingHurtEvent event){
|
||||
|
||||
if(!event.getEntity().worldObj.isRemote && event.getEntityLiving() instanceof EntityPlayer && !event.getSource().isUnblockable()
|
||||
&& !(event.getSource() instanceof IElementalDamage && ((IElementalDamage)event.getSource()).isRetaliatory())){
|
||||
WizardData data = WizardData.get((EntityPlayer)event.getEntityLiving());
|
||||
if(data != null){
|
||||
data.damageAllSoulboundCreatures(event.getAmount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import net.minecraft.world.World;
|
||||
|
||||
public class Flight extends Spell {
|
||||
|
||||
public Flight() {
|
||||
public Flight(){
|
||||
super(Tier.MASTER, 10, Element.EARTH, "flight", SpellType.UTILITY, 0, EnumAction.NONE, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryParticleType;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
@@ -24,11 +25,14 @@ import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
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. */
|
||||
/** The NBT tag name for storing the feared entity's UUID in the target's tag compound. */
|
||||
public static final String NBT_KEY = "fearedEntity";
|
||||
|
||||
public Intimidate() {
|
||||
@@ -101,5 +105,24 @@ public class Intimidate extends Spell {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
|
||||
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(NBT_KEY)){
|
||||
|
||||
Entity caster = WizardryUtilities.getEntityByUUID(creature.worldObj, entityNBT.getUniqueId(NBT_KEY));
|
||||
|
||||
if(caster instanceof EntityLivingBase){
|
||||
Intimidate.runAway(creature, (EntityLivingBase)caster);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import net.minecraft.world.World;
|
||||
|
||||
public class Levitation extends Spell {
|
||||
|
||||
public Levitation() {
|
||||
public Levitation(){
|
||||
super(Tier.ADVANCED, 10, Element.SORCERY, "levitation", SpellType.UTILITY, 0, EnumAction.BOW, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,14 @@ package electroblob.wizardry.spell;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryAchievements;
|
||||
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.monster.EntityCreeper;
|
||||
import net.minecraft.entity.passive.EntityPig;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -15,8 +18,15 @@ import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.EntityStruckByLightningEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class LightningBolt extends Spell {
|
||||
|
||||
/** The NBT key used to store the UUID of the player that summoned the lightning bolt. Used for achievements. */
|
||||
public static final String NBT_KEY = "summoningPlayer";
|
||||
|
||||
public LightningBolt() {
|
||||
super(Tier.ADVANCED, 40, Element.LIGHTNING, "lightning_bolt", SpellType.ATTACK, 80, EnumAction.NONE, false);
|
||||
@@ -46,7 +56,7 @@ public class LightningBolt extends Spell {
|
||||
|
||||
// Code for eventhandler recognition; for achievements and such like. Left in for future use.
|
||||
NBTTagCompound entityNBT = entitylightning.getEntityData();
|
||||
entityNBT.setUniqueId("summoningPlayer", caster.getUniqueID());
|
||||
entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID());
|
||||
}
|
||||
|
||||
caster.swingArm(hand);
|
||||
@@ -87,4 +97,22 @@ public class LightningBolt extends Spell {
|
||||
public boolean canBeCastByNPCs(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onEntityStruckByLightningEvent(EntityStruckByLightningEvent event){
|
||||
|
||||
if(event.getLightning().getEntityData() != null && event.getLightning().getEntityData().hasKey(NBT_KEY)){
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ 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.entity.living.EntityEvilWizard;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
@@ -26,7 +26,12 @@ import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
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
|
||||
@@ -44,10 +49,10 @@ public class MindControl extends Spell {
|
||||
|
||||
if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){
|
||||
|
||||
Entity target = rayTrace.entityHit;
|
||||
EntityLivingBase target = (EntityLivingBase)rayTrace.entityHit;
|
||||
|
||||
if(!world.isRemote){
|
||||
if(target instanceof EntityPlayer || !target.isNonBoss() || target instanceof INpc){
|
||||
if(!canControl(target)){
|
||||
// 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()));
|
||||
|
||||
@@ -87,8 +92,7 @@ public class MindControl extends Spell {
|
||||
|
||||
if(target != null){
|
||||
if(!world.isRemote){
|
||||
if(target instanceof EntityLiving && target.isNonBoss()
|
||||
&& !(target instanceof EntityWizard)){
|
||||
if(canControl(target)){
|
||||
|
||||
if(!MindControl.findMindControlTarget((EntityLiving)target, caster, world)){
|
||||
// If no valid target was found, this just acts like mind trick.
|
||||
@@ -118,12 +122,18 @@ public class MindControl extends Spell {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByNPCs(){
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Returns true if the given entity can be mind controlled (i.e. is not a player, npc, evil wizard or boss). */
|
||||
public static boolean canControl(EntityLivingBase target){
|
||||
return target instanceof EntityLiving && target.isNonBoss() && !(target instanceof INpc)
|
||||
&& !(target instanceof EntityEvilWizard);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -165,5 +175,42 @@ public class MindControl extends Spell {
|
||||
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
// 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
|
||||
}
|
||||
|
||||
}
|
||||
@SubscribeEvent
|
||||
public static void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){
|
||||
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.mind_control) && MindControl.canControl(event.getEntityLiving())){
|
||||
|
||||
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())) return;
|
||||
|
||||
if(caster instanceof EntityLivingBase){
|
||||
|
||||
if(MindControl.findMindControlTarget((EntityLiving)event.getEntityLiving(), (EntityLivingBase)caster, event.getEntity().worldObj)){
|
||||
// If it worked, skip setting the target to null.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,12 @@ import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class MindTrick extends Spell {
|
||||
|
||||
public MindTrick() {
|
||||
@@ -36,13 +41,13 @@ public class MindTrick extends Spell {
|
||||
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));
|
||||
}
|
||||
@@ -54,7 +59,7 @@ public class MindTrick extends Spell {
|
||||
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;
|
||||
@@ -68,14 +73,14 @@ public class MindTrick extends Spell {
|
||||
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++){
|
||||
@@ -85,16 +90,38 @@ public class MindTrick extends Spell {
|
||||
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;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingAttackEvent(LivingAttackEvent event){
|
||||
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 an (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,26 @@ package electroblob.wizardry.spell;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.util.IElementalDamage;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class ShadowWard extends Spell {
|
||||
|
||||
public ShadowWard() {
|
||||
@@ -20,20 +31,37 @@ public class ShadowWard extends Spell {
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingAttackEvent(LivingAttackEvent event){
|
||||
if(event.getSource() != null && event.getSource().getEntity() instanceof EntityLivingBase){
|
||||
// There used to be a check that the target was a player here, but I don't see any reason for it.
|
||||
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.getSource() instanceof IElementalDamage && ((IElementalDamage)event.getSource()).isRetaliatory())){
|
||||
|
||||
event.setCanceled(true);
|
||||
// Now we can preserve the original daage source (sort of) as long as we make it retaliatory.
|
||||
event.getEntityLiving().attackEntityFrom(MagicDamage.causeDirectMagicDamage(event.getSource().getEntity(), DamageType.MAGIC, true), event.getAmount()/2);
|
||||
((EntityLivingBase)event.getSource().getEntity()).attackEntityFrom(MagicDamage.causeDirectMagicDamage(event.getEntityLiving(), DamageType.MAGIC, true), event.getAmount()/2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,13 +8,19 @@ import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
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;
|
||||
import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class Transience extends Spell {
|
||||
|
||||
public Transience() {
|
||||
@@ -28,7 +34,7 @@ public class Transience extends Spell {
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) {
|
||||
|
||||
|
||||
if(!caster.isPotionActive(WizardryPotions.transience)){
|
||||
if(!world.isRemote){
|
||||
caster.addPotionEffect(new PotionEffect(WizardryPotions.transience, (int)(400*modifiers.get(WizardryItems.duration_upgrade)), 0));
|
||||
@@ -40,5 +46,37 @@ public class Transience extends Spell {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingAttackEvent(LivingAttackEvent event){
|
||||
if(event.getSource() != null){
|
||||
// Prevents all blockable damage while transience is active
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.transience) && !event.getSource().isUnblockable()){
|
||||
event.setCanceled(true);
|
||||
}
|
||||
// Prevents transient entities from causing any damage
|
||||
if(event.getSource().getEntity() instanceof EntityLivingBase
|
||||
&& ((EntityLivingBase)event.getSource().getEntity()).isPotionActive(WizardryPotions.transience)){
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onBlockPlaceEvent(BlockEvent.PlaceEvent event){
|
||||
// Prevents transient players from placing blocks
|
||||
if(event.getPlayer().isPotionActive(WizardryPotions.transience)){
|
||||
event.setCanceled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onBlockBreakEvent(BlockEvent.BreakEvent event){
|
||||
// Prevents transient players from breaking blocks
|
||||
if(event.getPlayer().isPotionActive(WizardryPotions.transience)){
|
||||
event.setCanceled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,25 +3,36 @@ package electroblob.wizardry.tileentity;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.event.SpellBindEvent;
|
||||
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.Spells;
|
||||
import electroblob.wizardry.registry.WizardryAchievements;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
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.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
public class ContainerArcaneWorkbench extends Container {
|
||||
|
||||
public TileEntityArcaneWorkbench tileEntityArcaneWorkbench;
|
||||
/** The arcane workbench tile entity associated with this container. */
|
||||
public TileEntityArcaneWorkbench tileentity;
|
||||
|
||||
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");
|
||||
@@ -29,6 +40,7 @@ public class ContainerArcaneWorkbench extends Container {
|
||||
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}},
|
||||
@@ -38,7 +50,7 @@ public class ContainerArcaneWorkbench extends Container {
|
||||
|
||||
public ContainerArcaneWorkbench(IInventory inventory, TileEntityArcaneWorkbench tileentity){
|
||||
|
||||
this.tileEntityArcaneWorkbench = tileentity;
|
||||
this.tileentity = tileentity;
|
||||
|
||||
ItemStack wand = tileentity.getStackInSlot(WAND_SLOT);
|
||||
|
||||
@@ -47,16 +59,16 @@ public class ContainerArcaneWorkbench extends Container {
|
||||
}
|
||||
|
||||
this.addSlotToContainer(new SlotItemList(tileentity, CRYSTAL_SLOT, 8, 88, 64, WizardryItems.magic_crystal))
|
||||
.setBackgroundName(EMPTY_SLOT_CRYSTAL.toString());
|
||||
|
||||
.setBackgroundName(EMPTY_SLOT_CRYSTAL.toString());
|
||||
|
||||
this.addSlotToContainer(new SlotWandArmour(tileentity, WAND_SLOT, 80, 64, this));
|
||||
|
||||
Set<Item> upgrades = new HashSet<Item>(WandHelper.getSpecialUpgrades());
|
||||
|
||||
Set<Item> upgrades = new HashSet<Item>(WandHelper.getSpecialUpgrades()); // Can't be done statically.
|
||||
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());
|
||||
.setBackgroundName(EMPTY_SLOT_UPGRADE.toString());
|
||||
|
||||
for(int x = 0; x < 9; x++){
|
||||
this.addSlotToContainer(new Slot(inventory, x, 8 + x * 18, 196));
|
||||
@@ -72,17 +84,14 @@ public class ContainerArcaneWorkbench extends Container {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInteractWith(EntityPlayer par1EntityPlayer)
|
||||
{
|
||||
return this.tileEntityArcaneWorkbench.isUseableByPlayer(par1EntityPlayer);
|
||||
public boolean canInteractWith(EntityPlayer player){
|
||||
return this.tileentity.isUseableByPlayer(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the central wand/armour slot when its item is changed or removed.
|
||||
*/
|
||||
// I wrote this!
|
||||
/** Called from the central wand/armour slot when its item is changed or removed. */
|
||||
// In case I forget again and think it should have @Override: 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)){
|
||||
@@ -110,8 +119,8 @@ public class ContainerArcaneWorkbench extends Container {
|
||||
if(stack.getItem() == WizardryItems.blank_scroll){
|
||||
// If a blank scroll is added
|
||||
// The first slot is shown
|
||||
this.getSlot(0).xDisplayPosition = ContainerArcaneWorkbench.SPELL_BOOK_SLOT_COORDS[0][0][0];
|
||||
this.getSlot(0).yDisplayPosition = ContainerArcaneWorkbench.SPELL_BOOK_SLOT_COORDS[0][0][1];
|
||||
this.getSlot(0).xDisplayPosition = SPELL_BOOK_SLOT_COORDS[0][0][0];
|
||||
this.getSlot(0).yDisplayPosition = SPELL_BOOK_SLOT_COORDS[0][0][1];
|
||||
|
||||
// The rest of the slots are hidden
|
||||
for(int i=1; i<CRYSTAL_SLOT; i++){
|
||||
@@ -138,7 +147,7 @@ public class ContainerArcaneWorkbench extends Container {
|
||||
for(int i=0; i<CRYSTAL_SLOT; i++){
|
||||
|
||||
int n = WandHelper.getUpgradeLevel(stack, WizardryItems.attunement_upgrade);
|
||||
int[] coords = ContainerArcaneWorkbench.SPELL_BOOK_SLOT_COORDS[n][i];
|
||||
int[] coords = SPELL_BOOK_SLOT_COORDS[n][i];
|
||||
|
||||
Slot slot1 = this.getSlot(i);
|
||||
// Puts the slot back in the correct position
|
||||
@@ -165,56 +174,49 @@ public class ContainerArcaneWorkbench extends Container {
|
||||
|
||||
// FIXME: It only seems to be syncing correctly when a stack is put into the slot, not taken out.
|
||||
// Was this broken in 1.7.10 as well?
|
||||
this.tileEntityArcaneWorkbench.sync();
|
||||
this.tileentity.sync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack transferStackInSlot(EntityPlayer par1EntityPlayer, int clickedSlotId)
|
||||
public ItemStack transferStackInSlot(EntityPlayer player, int clickedSlotId)
|
||||
{
|
||||
ItemStack remainder = null;
|
||||
Slot slot = (Slot)this.inventorySlots.get(clickedSlotId);
|
||||
|
||||
if (slot != null && slot.getHasStack())
|
||||
{
|
||||
ItemStack itemstack = slot.getStack(); // The stack that was there originally
|
||||
remainder = itemstack.copy(); // A copy of that stack
|
||||
ItemStack stack = slot.getStack(); // The stack that was there originally
|
||||
remainder = stack.copy(); // A copy of that stack
|
||||
|
||||
// Workbench -> inventory
|
||||
if(clickedSlotId <= UPGRADE_SLOT){
|
||||
// Tries to move the stack into the player's inventory. If this fails...
|
||||
if (!this.mergeItemStack(itemstack, UPGRADE_SLOT + 1, this.inventorySlots.size(), true)){
|
||||
if (!this.mergeItemStack(stack, UPGRADE_SLOT + 1, this.inventorySlots.size(), true)){
|
||||
return null; // ...nothing else happens.
|
||||
}
|
||||
|
||||
// Inventory -> workbench
|
||||
}else{
|
||||
}
|
||||
// Inventory -> workbench
|
||||
else{
|
||||
// The following logic prevents shift-clicking transferring the items to the wrong slot.
|
||||
int minSlotId = 0;
|
||||
int maxSlotId = UPGRADE_SLOT;
|
||||
|
||||
if(itemstack.getItem() instanceof ItemSpellBook){
|
||||
if(stack.getItem() instanceof ItemSpellBook){
|
||||
minSlotId = 0;
|
||||
maxSlotId = CRYSTAL_SLOT-1;
|
||||
}
|
||||
else if(itemstack.getItem() == WizardryItems.magic_crystal){
|
||||
else if(stack.getItem() == WizardryItems.magic_crystal){
|
||||
minSlotId = CRYSTAL_SLOT;
|
||||
maxSlotId = CRYSTAL_SLOT;
|
||||
}
|
||||
else if(itemstack.getItem() instanceof ItemWand || itemstack.getItem() instanceof ItemWizardArmour
|
||||
|| itemstack.getItem() == WizardryItems.blank_scroll){
|
||||
else if(stack.getItem() instanceof ItemWand || stack.getItem() instanceof ItemWizardArmour
|
||||
|| stack.getItem() == WizardryItems.blank_scroll){
|
||||
minSlotId = WAND_SLOT;
|
||||
maxSlotId = WAND_SLOT;
|
||||
}
|
||||
else if(itemstack.getItem() instanceof ItemArcaneTome
|
||||
|| itemstack.getItem() instanceof ItemArmourUpgrade
|
||||
|| itemstack.getItem() == WizardryItems.condenser_upgrade
|
||||
|| itemstack.getItem() == WizardryItems.siphon_upgrade
|
||||
|| itemstack.getItem() == WizardryItems.range_upgrade
|
||||
|| itemstack.getItem() == WizardryItems.cooldown_upgrade
|
||||
|| itemstack.getItem() == WizardryItems.duration_upgrade
|
||||
|| itemstack.getItem() == WizardryItems.storage_upgrade
|
||||
|| itemstack.getItem() == WizardryItems.blast_upgrade
|
||||
|| itemstack.getItem() == WizardryItems.attunement_upgrade){
|
||||
else if(stack.getItem() instanceof ItemArcaneTome
|
||||
|| stack.getItem() instanceof ItemArmourUpgrade
|
||||
|| WandHelper.isWandUpgrade(stack.getItem())){
|
||||
minSlotId = UPGRADE_SLOT;
|
||||
maxSlotId = UPGRADE_SLOT;
|
||||
}
|
||||
@@ -222,13 +224,13 @@ public class ContainerArcaneWorkbench extends Container {
|
||||
return null; // If none of the above cases were true, then the item won't fit in the workbench.
|
||||
}
|
||||
|
||||
if(!this.mergeItemStack(itemstack, minSlotId, maxSlotId + 1, false))
|
||||
if(!this.mergeItemStack(stack, minSlotId, maxSlotId + 1, false))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemstack.stackSize == 0)
|
||||
if (stack.stackSize == 0)
|
||||
{
|
||||
slot.putStack((ItemStack)null);
|
||||
}
|
||||
@@ -237,12 +239,12 @@ public class ContainerArcaneWorkbench extends Container {
|
||||
slot.onSlotChanged();
|
||||
}
|
||||
|
||||
if (itemstack.stackSize == remainder.stackSize)
|
||||
if (stack.stackSize == remainder.stackSize)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
slot.onPickupFromSlot(par1EntityPlayer, itemstack);
|
||||
slot.onPickupFromSlot(player, stack);
|
||||
}
|
||||
|
||||
return remainder;
|
||||
@@ -262,4 +264,198 @@ public class ContainerArcaneWorkbench extends Container {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Called (via {@link electroblob.wizardry.packet.PacketControlInput PacketControlInput}) when the apply button
|
||||
* in the arcane workbench GUI is pressed. */
|
||||
// All operations on the items contained in the inventory simply call the corresponding methods in the tileentity.
|
||||
// As of 2.1, for the sake of events and neatness of code, this was moved here from TileEntityArcaneWorkbench.
|
||||
public void onApplyButtonPressed(EntityPlayer player){
|
||||
|
||||
ItemStack wand = this.getSlot(WAND_SLOT).getStack();
|
||||
ItemStack[] spellBooks = new ItemStack[CRYSTAL_SLOT];
|
||||
for(int i=0; i<spellBooks.length; i++){
|
||||
spellBooks[i] = this.getSlot(i).getStack();
|
||||
}
|
||||
ItemStack crystals = this.getSlot(CRYSTAL_SLOT).getStack();
|
||||
ItemStack upgrade = this.getSlot(UPGRADE_SLOT).getStack();
|
||||
|
||||
if(MinecraftForge.EVENT_BUS.post(new SpellBindEvent(player, this))) return;
|
||||
|
||||
// Since the workbench now accepts armour as well as wands, this check is needed.
|
||||
if(wand != null && wand.getItem() instanceof ItemWand){
|
||||
|
||||
// Upgrades wand if necessary. Damage is copied, preserving remaining durability,
|
||||
// and also the entire NBT tag compound.
|
||||
if(upgrade != null){
|
||||
|
||||
if(upgrade.getItem() == WizardryItems.arcane_tome){
|
||||
|
||||
ItemStack newWand;
|
||||
|
||||
switch(Tier.values()[upgrade.getItemDamage()]){
|
||||
|
||||
case APPRENTICE:
|
||||
if(((ItemWand)wand.getItem()).tier == Tier.BASIC){
|
||||
newWand = new ItemStack(WizardryUtilities.getWand(Tier.values()[upgrade.getItemDamage()], ((ItemWand)wand.getItem()).element));
|
||||
newWand.setTagCompound(wand.getTagCompound());
|
||||
// This needs to be done after copying the tag compound so the max damage for the new wand takes storage
|
||||
// upgrades into account.
|
||||
newWand.setItemDamage(newWand.getMaxDamage() - (wand.getMaxDamage() - wand.getItemDamage()));
|
||||
this.putStackInSlot(WAND_SLOT, newWand);
|
||||
this.putStackInSlot(UPGRADE_SLOT, null);
|
||||
player.addStat(WizardryAchievements.apprentice, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case ADVANCED:
|
||||
if(((ItemWand)wand.getItem()).tier == Tier.APPRENTICE){
|
||||
newWand = new ItemStack(WizardryUtilities.getWand(Tier.values()[upgrade.getItemDamage()], ((ItemWand)wand.getItem()).element));
|
||||
newWand.setTagCompound(wand.getTagCompound());
|
||||
newWand.setItemDamage(newWand.getMaxDamage() - (wand.getMaxDamage() - wand.getItemDamage()));
|
||||
this.putStackInSlot(WAND_SLOT, newWand);
|
||||
this.putStackInSlot(UPGRADE_SLOT, null);
|
||||
}
|
||||
break;
|
||||
|
||||
case MASTER:
|
||||
if(((ItemWand)wand.getItem()).tier == Tier.ADVANCED){
|
||||
newWand = new ItemStack(WizardryUtilities.getWand(Tier.values()[upgrade.getItemDamage()], ((ItemWand)wand.getItem()).element));
|
||||
newWand.setTagCompound(wand.getTagCompound());
|
||||
newWand.setItemDamage(newWand.getMaxDamage() - (wand.getMaxDamage() - wand.getItemDamage()));
|
||||
this.putStackInSlot(WAND_SLOT, newWand);
|
||||
this.putStackInSlot(UPGRADE_SLOT, null);
|
||||
player.addStat(WizardryAchievements.master, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// This needs to happen so the charging works on the new wand, not the old one.
|
||||
wand = this.getSlot(WAND_SLOT).getStack();
|
||||
|
||||
}else{
|
||||
|
||||
// Special upgrades
|
||||
|
||||
// Used to preserve existing mana when upgrading storage rather than creating free mana.
|
||||
int prevMana = wand.getMaxDamage() - wand.getItemDamage();
|
||||
|
||||
if(WandHelper.getTotalUpgrades(wand) < ((ItemWand)wand.getItem()).tier.upgradeLimit
|
||||
&& WandHelper.getUpgradeLevel(wand, upgrade.getItem()) < Constants.UPGRADE_STACK_LIMIT){
|
||||
|
||||
WandHelper.applyUpgrade(wand, upgrade.getItem());
|
||||
|
||||
// Special behaviours for specific upgrades
|
||||
if(upgrade.getItem() == WizardryItems.storage_upgrade){
|
||||
wand.setItemDamage(wand.getMaxDamage() - prevMana);
|
||||
}
|
||||
if(upgrade.getItem() == WizardryItems.attunement_upgrade){
|
||||
|
||||
Spell[] spells = WandHelper.getSpells(wand);
|
||||
Spell[] newSpells = new Spell[5 + WandHelper.getUpgradeLevel(wand, WizardryItems.attunement_upgrade)];
|
||||
|
||||
for(int i=0; i<newSpells.length; i++){
|
||||
// Prevents both NPEs and AIOOBEs
|
||||
newSpells[i] = i < spells.length && spells[i] != null ? spells[i] : Spells.none;
|
||||
}
|
||||
|
||||
WandHelper.setSpells(wand, newSpells);
|
||||
|
||||
int[] cooldown = WandHelper.getCooldowns(wand);
|
||||
int[] newCooldown = new int[5 + WandHelper.getUpgradeLevel(wand, WizardryItems.attunement_upgrade)];
|
||||
|
||||
if(cooldown.length > 0){
|
||||
for(int i=0; i<cooldown.length; i++){
|
||||
newCooldown[i] = cooldown[i];
|
||||
}
|
||||
}
|
||||
|
||||
WandHelper.setCooldowns(wand, newCooldown);
|
||||
}
|
||||
|
||||
this.getSlot(UPGRADE_SLOT).decrStackSize(1);
|
||||
player.addStat(WizardryAchievements.special_upgrade, 1);
|
||||
|
||||
if(WandHelper.getTotalUpgrades(wand) == Tier.MASTER.upgradeLimit){
|
||||
player.addStat(WizardryAchievements.max_out_wand, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reads NBT spell id array to variable, edits this, then writes it back to NBT.
|
||||
// Original spells are preserved; if a slot is left empty the existing spell binding will remain.
|
||||
// Accounts for spells which cannot be applied because they are above the wand's tier; these spells
|
||||
// will not bind but the existing spell in that slot will remain and other applicable spells will
|
||||
// be bound as normal, along with any upgrades and crystals.
|
||||
Spell[] spells = WandHelper.getSpells(wand);
|
||||
if(spells.length <= 0){
|
||||
// 5 here because if the spell array doesn't exist, the wand can't possibly have attunement upgrades
|
||||
spells = new Spell[5];
|
||||
}
|
||||
for(int i=0; i<spells.length; i++){
|
||||
if(spellBooks[i] != null && !(Spell.get(spellBooks[i].getItemDamage()).tier.level > ((ItemWand)wand.getItem()).tier.level)){
|
||||
spells[i] = Spell.get(spellBooks[i].getItemDamage());
|
||||
}
|
||||
}
|
||||
WandHelper.setSpells(wand, spells);
|
||||
|
||||
// Charges wand by appropriate amount
|
||||
if(crystals != null){
|
||||
int chargeDepleted = wand.getItemDamage();
|
||||
//System.out.println("Charge depleted: " + chargeDepleted);
|
||||
//System.out.println("Crystals found: " + crystals.stackSize);
|
||||
if(crystals.stackSize * Constants.MANA_PER_CRYSTAL < chargeDepleted){
|
||||
//System.out.println("charging");
|
||||
wand.setItemDamage(chargeDepleted - crystals.stackSize * Constants.MANA_PER_CRYSTAL);
|
||||
this.getSlot(CRYSTAL_SLOT).decrStackSize(crystals.stackSize);
|
||||
}else if(chargeDepleted != 0){
|
||||
//System.out.println((int)Math.ceil(((double)chargeDepleted)/50));
|
||||
this.getSlot(CRYSTAL_SLOT).decrStackSize((int)Math.ceil(((double)chargeDepleted)/Constants.MANA_PER_CRYSTAL));
|
||||
wand.setItemDamage(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Armour
|
||||
else if(wand != null && wand.getItem() instanceof ItemWizardArmour){
|
||||
// Applies legendary upgrade
|
||||
if(upgrade != null && upgrade.getItem() == WizardryItems.armour_upgrade){
|
||||
if(!wand.hasTagCompound()){
|
||||
wand.setTagCompound(new NBTTagCompound());
|
||||
}
|
||||
if(!wand.getTagCompound().hasKey("legendary")){
|
||||
wand.getTagCompound().setBoolean("legendary", true);
|
||||
this.putStackInSlot(UPGRADE_SLOT, null);
|
||||
player.addStat(WizardryAchievements.legendary);
|
||||
}
|
||||
}
|
||||
// Charges armour by appropriate amount
|
||||
if(crystals != null){
|
||||
int chargeDepleted = wand.getItemDamage();
|
||||
if(crystals.stackSize * Constants.MANA_PER_CRYSTAL < chargeDepleted){
|
||||
wand.setItemDamage(chargeDepleted - crystals.stackSize * Constants.MANA_PER_CRYSTAL);
|
||||
this.getSlot(CRYSTAL_SLOT).decrStackSize(crystals.stackSize);
|
||||
}else if(chargeDepleted != 0){
|
||||
this.getSlot(CRYSTAL_SLOT).decrStackSize((int)Math.ceil(((double)chargeDepleted)/Constants.MANA_PER_CRYSTAL));
|
||||
wand.setItemDamage(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scrolls
|
||||
else if(wand != null && wand.getItem() == WizardryItems.blank_scroll){
|
||||
// Spells can only be bound to scrolls if the player has already cast them (prevents casting of master spells without getting a master wand)
|
||||
// This restriction does not apply in creative mode
|
||||
if(spellBooks[0] != null && (player.capabilities.isCreativeMode || (WizardData.get(player) != null
|
||||
&& WizardData.get(player).hasSpellBeenDiscovered(Spell.get(spellBooks[0].getItemDamage()))))
|
||||
&& crystals != null && crystals.stackSize * Constants.MANA_PER_CRYSTAL > Spell.get(spellBooks[0].getItemDamage()).cost){
|
||||
|
||||
this.getSlot(CRYSTAL_SLOT).decrStackSize((int)Math.ceil(((double)Spell.get(spellBooks[0].getItemDamage()).cost)/Constants.MANA_PER_CRYSTAL));
|
||||
this.putStackInSlot(WAND_SLOT, new ItemStack(WizardryItems.scroll, 1, spellBooks[0].getItemDamage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,18 +3,11 @@ package electroblob.wizardry.tileentity;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.item.ItemWizardArmour;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryAchievements;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
@@ -286,196 +279,4 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory,
|
||||
}
|
||||
}
|
||||
|
||||
/** Called (via {@link electroblob.wizardry.packet.PacketControlInput PacketControlInput}) when the apply button
|
||||
* in the arcane workbench GUI is pressed. */
|
||||
// IDEA: Perhaps this should be in the container class?
|
||||
public void onApplyButtonPressed(EntityPlayer player){
|
||||
|
||||
ItemStack wand = this.getStackInSlot(ContainerArcaneWorkbench.WAND_SLOT);
|
||||
ItemStack[] spellBooks = new ItemStack[ContainerArcaneWorkbench.CRYSTAL_SLOT];
|
||||
for(int i=0; i<spellBooks.length; i++){
|
||||
spellBooks[i] = this.getStackInSlot(i);
|
||||
}
|
||||
ItemStack crystals = this.getStackInSlot(ContainerArcaneWorkbench.CRYSTAL_SLOT);
|
||||
ItemStack upgrade = this.getStackInSlot(ContainerArcaneWorkbench.UPGRADE_SLOT);
|
||||
|
||||
// TODO: Implement this
|
||||
//if(MinecraftForge.EVENT_BUS.post(new SpellBindEvent(player))) return;
|
||||
|
||||
// Since the workbench now accepts armour as well as wands, this check is needed.
|
||||
if(wand != null && wand.getItem() instanceof ItemWand){
|
||||
|
||||
// Upgrades wand if necessary. Damage is copied, preserving remaining durability,
|
||||
// and also the entire NBT tag compound.
|
||||
if(upgrade != null){
|
||||
|
||||
if(upgrade.getItem() == WizardryItems.arcane_tome){
|
||||
|
||||
ItemStack newWand;
|
||||
|
||||
switch(Tier.values()[upgrade.getItemDamage()]){
|
||||
|
||||
case APPRENTICE:
|
||||
if(((ItemWand)wand.getItem()).tier == Tier.BASIC){
|
||||
newWand = new ItemStack(WizardryUtilities.getWand(Tier.values()[upgrade.getItemDamage()], ((ItemWand)wand.getItem()).element));
|
||||
newWand.setTagCompound(wand.getTagCompound());
|
||||
// This needs to be done after copying the tag compound so the max damage for the new wand takes storage
|
||||
// upgrades into account.
|
||||
newWand.setItemDamage(newWand.getMaxDamage() - (wand.getMaxDamage() - wand.getItemDamage()));
|
||||
this.setInventorySlotContents(ContainerArcaneWorkbench.WAND_SLOT, newWand);
|
||||
this.setInventorySlotContents(ContainerArcaneWorkbench.UPGRADE_SLOT, null);
|
||||
player.addStat(WizardryAchievements.apprentice, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case ADVANCED:
|
||||
if(((ItemWand)wand.getItem()).tier == Tier.APPRENTICE){
|
||||
newWand = new ItemStack(WizardryUtilities.getWand(Tier.values()[upgrade.getItemDamage()], ((ItemWand)wand.getItem()).element));
|
||||
newWand.setTagCompound(wand.getTagCompound());
|
||||
newWand.setItemDamage(newWand.getMaxDamage() - (wand.getMaxDamage() - wand.getItemDamage()));
|
||||
this.setInventorySlotContents(ContainerArcaneWorkbench.WAND_SLOT, newWand);
|
||||
this.setInventorySlotContents(ContainerArcaneWorkbench.UPGRADE_SLOT, null);
|
||||
}
|
||||
break;
|
||||
|
||||
case MASTER:
|
||||
if(((ItemWand)wand.getItem()).tier == Tier.ADVANCED){
|
||||
newWand = new ItemStack(WizardryUtilities.getWand(Tier.values()[upgrade.getItemDamage()], ((ItemWand)wand.getItem()).element));
|
||||
newWand.setTagCompound(wand.getTagCompound());
|
||||
newWand.setItemDamage(newWand.getMaxDamage() - (wand.getMaxDamage() - wand.getItemDamage()));
|
||||
this.setInventorySlotContents(ContainerArcaneWorkbench.WAND_SLOT, newWand);
|
||||
this.setInventorySlotContents(ContainerArcaneWorkbench.UPGRADE_SLOT, null);
|
||||
player.addStat(WizardryAchievements.master, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// This needs to happen so the charging works on the new wand, not the old one.
|
||||
wand = this.getStackInSlot(ContainerArcaneWorkbench.WAND_SLOT);
|
||||
|
||||
}else{
|
||||
|
||||
// Special upgrades
|
||||
|
||||
// Used to preserve existing mana when upgrading storage rather than creating free mana.
|
||||
int prevMana = wand.getMaxDamage() - wand.getItemDamage();
|
||||
|
||||
if(WandHelper.getTotalUpgrades(wand) < ((ItemWand)wand.getItem()).tier.upgradeLimit
|
||||
&& WandHelper.getUpgradeLevel(wand, upgrade.getItem()) < Constants.UPGRADE_STACK_LIMIT){
|
||||
|
||||
WandHelper.applyUpgrade(wand, upgrade.getItem());
|
||||
|
||||
// Special behaviours for specific upgrades
|
||||
if(upgrade.getItem() == WizardryItems.storage_upgrade){
|
||||
wand.setItemDamage(wand.getMaxDamage() - prevMana);
|
||||
}
|
||||
if(upgrade.getItem() == WizardryItems.attunement_upgrade){
|
||||
|
||||
Spell[] spells = WandHelper.getSpells(wand);
|
||||
Spell[] newSpells = new Spell[5 + WandHelper.getUpgradeLevel(wand, WizardryItems.attunement_upgrade)];
|
||||
|
||||
for(int i=0; i<newSpells.length; i++){
|
||||
// Prevents both NPEs and AIOOBEs
|
||||
newSpells[i] = i < spells.length && spells[i] != null ? spells[i] : Spells.none;
|
||||
}
|
||||
|
||||
WandHelper.setSpells(wand, newSpells);
|
||||
|
||||
int[] cooldown = WandHelper.getCooldowns(wand);
|
||||
int[] newCooldown = new int[5 + WandHelper.getUpgradeLevel(wand, WizardryItems.attunement_upgrade)];
|
||||
|
||||
if(cooldown.length > 0){
|
||||
for(int i=0; i<cooldown.length; i++){
|
||||
newCooldown[i] = cooldown[i];
|
||||
}
|
||||
}
|
||||
|
||||
WandHelper.setCooldowns(wand, newCooldown);
|
||||
}
|
||||
|
||||
this.decrStackSize(ContainerArcaneWorkbench.UPGRADE_SLOT, 1);
|
||||
player.addStat(WizardryAchievements.special_upgrade, 1);
|
||||
|
||||
if(WandHelper.getTotalUpgrades(wand) == Tier.MASTER.upgradeLimit){
|
||||
player.addStat(WizardryAchievements.max_out_wand, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reads NBT spell id array to variable, edits this, then writes it back to NBT.
|
||||
// Original spells are preserved; if a slot is left empty the existing spell binding will remain.
|
||||
// Accounts for spells which cannot be applied because they are above the wand's tier; these spells
|
||||
// will not bind but the existing spell in that slot will remain and other applicable spells will
|
||||
// be bound as normal, along with any upgrades and crystals.
|
||||
Spell[] spells = WandHelper.getSpells(wand);
|
||||
if(spells.length <= 0){
|
||||
// 5 here because if the spell array doesn't exist, the wand can't possibly have attunement upgrades
|
||||
spells = new Spell[5];
|
||||
}
|
||||
for(int i=0; i<spells.length; i++){
|
||||
if(spellBooks[i] != null && !(Spell.get(spellBooks[i].getItemDamage()).tier.level > ((ItemWand)wand.getItem()).tier.level)){
|
||||
spells[i] = Spell.get(spellBooks[i].getItemDamage());
|
||||
}
|
||||
}
|
||||
WandHelper.setSpells(wand, spells);
|
||||
|
||||
// Charges wand by appropriate amount
|
||||
if(crystals != null){
|
||||
int chargeDepleted = wand.getItemDamage();
|
||||
//System.out.println("Charge depleted: " + chargeDepleted);
|
||||
//System.out.println("Crystals found: " + crystals.stackSize);
|
||||
if(crystals.stackSize * Constants.MANA_PER_CRYSTAL < chargeDepleted){
|
||||
//System.out.println("charging");
|
||||
wand.setItemDamage(chargeDepleted - crystals.stackSize * Constants.MANA_PER_CRYSTAL);
|
||||
this.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, crystals.stackSize);
|
||||
}else if(chargeDepleted != 0){
|
||||
//System.out.println((int)Math.ceil(((double)chargeDepleted)/50));
|
||||
this.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, (int)Math.ceil(((double)chargeDepleted)/Constants.MANA_PER_CRYSTAL));
|
||||
wand.setItemDamage(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Armour
|
||||
}else if(wand != null && wand.getItem() instanceof ItemWizardArmour){
|
||||
// Applies legendary upgrade
|
||||
if(upgrade != null && upgrade.getItem() == WizardryItems.armour_upgrade){
|
||||
if(!wand.hasTagCompound()){
|
||||
wand.setTagCompound(new NBTTagCompound());
|
||||
}
|
||||
if(!wand.getTagCompound().hasKey("legendary")){
|
||||
wand.getTagCompound().setBoolean("legendary", true);
|
||||
this.setInventorySlotContents(ContainerArcaneWorkbench.UPGRADE_SLOT, null);
|
||||
player.addStat(WizardryAchievements.legendary);
|
||||
}
|
||||
}
|
||||
// Charges armour by appropriate amount
|
||||
if(crystals != null){
|
||||
int chargeDepleted = wand.getItemDamage();
|
||||
if(crystals.stackSize * Constants.MANA_PER_CRYSTAL < chargeDepleted){
|
||||
wand.setItemDamage(chargeDepleted - crystals.stackSize * Constants.MANA_PER_CRYSTAL);
|
||||
this.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, crystals.stackSize);
|
||||
}else if(chargeDepleted != 0){
|
||||
this.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, (int)Math.ceil(((double)chargeDepleted)/Constants.MANA_PER_CRYSTAL));
|
||||
wand.setItemDamage(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Scrolls
|
||||
}else if(wand != null && wand.getItem() == WizardryItems.blank_scroll){
|
||||
// Spells can only be bound to scrolls if the player has already cast them (prevents casting of master spells without getting a master wand)
|
||||
// This restriction does not apply in creative mode
|
||||
if(spellBooks[0] != null && (player.capabilities.isCreativeMode || (WizardData.get(player) != null
|
||||
&& WizardData.get(player).hasSpellBeenDiscovered(Spell.get(spellBooks[0].getItemDamage()))))
|
||||
&& crystals != null && crystals.stackSize * Constants.MANA_PER_CRYSTAL > Spell.get(spellBooks[0].getItemDamage()).cost){
|
||||
|
||||
this.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, (int)Math.ceil(((double)Spell.get(spellBooks[0].getItemDamage()).cost)/Constants.MANA_PER_CRYSTAL));
|
||||
this.setInventorySlotContents(ContainerArcaneWorkbench.WAND_SLOT, new ItemStack(WizardryItems.scroll, 1, spellBooks[0].getItemDamage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
package electroblob.wizardry.util;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryAchievements;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import net.minecraft.entity.monster.EntityCreeper;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
/** This interface allows {@link MagicDamage} and {@link IndirectMagicDamage} to both be treated as instances of a
|
||||
* single type so that the damage type field can be accessed, rather than having to deal with each of them separately,
|
||||
@@ -8,10 +14,34 @@ import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
* need to extend different subclasses of {@link net.minecraft.util.DamageSource DamageSource}).
|
||||
* @since Wizardry 1.1
|
||||
* @author Electroblob */
|
||||
@Mod.EventBusSubscriber
|
||||
public interface IElementalDamage {
|
||||
|
||||
|
||||
DamageType getType();
|
||||
|
||||
|
||||
boolean isRetaliatory();
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingAttackEvent(LivingAttackEvent event){
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,22 +5,24 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
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
|
||||
* Object that wraps any number of spell modifiers 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).
|
||||
* It is possible to calculate spell modifiers from wand NBT within the cast methods, but this is cumbersome and does
|
||||
* not allow the modifiers to be sent to the client, which is sometimes necessary (for example, detonate needs to know
|
||||
* about range modifiers on the client side or the particles wouldn't show outside of the base range).
|
||||
* <p>
|
||||
* 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
|
||||
* Most external interaction with SpellModifiers objects will be in {@link SpellCastEvent.Pre}, where you can add additional
|
||||
* modifiers to them if desired for use with your own spells, or modify the existing ones. If you have added a wand
|
||||
* upgrade, this is <b>not</b> done automatically for you; you will have to do it yourself (for the simple reason that
|
||||
* not all wand upgrades affect spells).
|
||||
* not all wand upgrades affect spells). SpellModifiers objects are <i>mutable</i>, so you can simply change the values
|
||||
* they contain to modify the spell.
|
||||
* <p>
|
||||
* To use a SpellModifiers object within the <code>Spell.cast</code> methods, simply retrieve the desired multiplier
|
||||
* using {@link SpellModifiers#get(Item)} for wand upgrades, or {@link SpellModifiers#get(String)} if the multiplier
|
||||
@@ -29,13 +31,13 @@ import net.minecraftforge.fml.common.network.ByteBufUtils;
|
||||
* @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
|
||||
// I have made the decision that the USERS of this class must decide whether the modifiers 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. */
|
||||
/** Constant string identifier for the damage modifier. All the other modifiers in Wizardry have items. */
|
||||
public static final String DAMAGE = "damage";
|
||||
|
||||
private Map<String, Float> multiplierMap;
|
||||
@@ -53,7 +55,7 @@ public final class SpellModifiers {
|
||||
* 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
|
||||
* @param multiplier The multiplier value, with 1 being default. Usage of modifiers is up to individual spells to
|
||||
* implement.
|
||||
* @param needsSyncing Whether this multiplier should be synchronised with the client via packets. <i>Only set this
|
||||
* to true if particles will be spawned which need to know the value of the multiplier.</i>
|
||||
@@ -69,7 +71,7 @@ public final class SpellModifiers {
|
||||
* 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
|
||||
* @param multiplier The multiplier value, with 1 being default. Usage of modifiers is up to individual spells to
|
||||
* implement.
|
||||
* @param needsSyncing Whether this multiplier should be synchronised with the client via packets. <i>Only set this
|
||||
* to true if particles will be spawned which depend on the multiplier.</i>
|
||||
|
||||
@@ -23,7 +23,9 @@ import net.minecraft.nbt.NBTTagCompound;
|
||||
* unlikely case that this is necessary.
|
||||
* <p>
|
||||
* 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!
|
||||
* can, for example, pass in a stack of snowballs without causing problems, but that is of course pointless! However,
|
||||
* if you have your own spell casting item (which doesn't extend ItemWand), this setup means you can still use this class
|
||||
* to manage its NBT structure.
|
||||
* <p>
|
||||
* All <b>get</b> 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.<br>
|
||||
|
||||
@@ -6,6 +6,7 @@ import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import electroblob.wizardry.spell.Clairvoyance;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.pathfinding.NodeProcessor;
|
||||
import net.minecraft.pathfinding.Path;
|
||||
@@ -14,7 +15,8 @@ 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. */
|
||||
/** Minecraft's pathfinder refused to play nicely, so I 'borrowed' its code and fiddled with it. Currently this is only
|
||||
* used for the {@link Clairvoyance} spell. */
|
||||
public class WizardryPathFinder {
|
||||
|
||||
/** The path being generated */
|
||||
|
||||
@@ -56,6 +56,14 @@ 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.
|
||||
* Broadly speaking, these fall into the following categories:
|
||||
* <p>
|
||||
* - In-world utilities (position calculating, retrieving entities, etc.)<br>
|
||||
* - Raytracing<br>
|
||||
* - Drawing utilities (client-only)<br>
|
||||
* - NBT and data storage utilities<br>
|
||||
* - Interaction with the ally designation system<br>
|
||||
* - Loot and weighting utilities
|
||||
* @see CommonProxy
|
||||
* @see WandHelper
|
||||
* @since Wizardry 1.0
|
||||
|
||||
Reference in New Issue
Block a user