From: notzed@gmail.com Date: Thu, 28 Feb 2013 10:33:46 +0000 (+0000) Subject: Initial import. X-Git-Tag: dusk-0.1~14 X-Git-Url: https://code.zedzone.au/cvs?a=commitdiff_plain;h=fbf8a5302dc4d5cb0b21c51acd9bf80d821debc6;p=duskz Initial import. git-svn-id: file:///home/notzed/svn/duskz/trunk@2 b8b59bfb-1aa4-4687-8f88-a62eeb14c21e --- diff --git a/DuskCommon/COPYING b/DuskCommon/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/DuskCommon/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/DuskCommon/README b/DuskCommon/README new file mode 100644 index 0000000..2df1b47 --- /dev/null +++ b/DuskCommon/README @@ -0,0 +1,32 @@ + +README +------ +This is a library containing some common utilities and classes betwen +server and client. + +This is primarily classes and utilities for handling the client<>server +protocol. + +LICENSE +------- + DuskZ is free software, see COPYING for your rights. + + Some files are under other compatible licenses. + + Copyright (C) 2000 Tom Weingarten + Copyright (C) 2013 Michael Zucchi + + DuskZ is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + DuskZ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DuskZ; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + \ No newline at end of file diff --git a/DuskCommon/build.xml b/DuskCommon/build.xml new file mode 100644 index 0000000..23edae7 --- /dev/null +++ b/DuskCommon/build.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + Builds, tests, and runs the project DuskCommon. + + + diff --git a/DuskCommon/nbproject/build-impl.xml b/DuskCommon/nbproject/build-impl.xml new file mode 100644 index 0000000..625b5c6 --- /dev/null +++ b/DuskCommon/nbproject/build-impl.xml @@ -0,0 +1,1411 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DuskCommon/nbproject/genfiles.properties b/DuskCommon/nbproject/genfiles.properties new file mode 100644 index 0000000..b7dd181 --- /dev/null +++ b/DuskCommon/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=cb9774bc +build.xml.script.CRC32=41ed8886 +build.xml.stylesheet.CRC32=28e38971@1.56.1.46 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=cb9774bc +nbproject/build-impl.xml.script.CRC32=4cf1c99d +nbproject/build-impl.xml.stylesheet.CRC32=c6d2a60f@1.56.1.46 diff --git a/DuskCommon/nbproject/project.properties b/DuskCommon/nbproject/project.properties new file mode 100644 index 0000000..0f9dd09 --- /dev/null +++ b/DuskCommon/nbproject/project.properties @@ -0,0 +1,70 @@ +project.license=dusk +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processor.options= +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/DuskCommon.jar +dist.javadoc.dir=${dist.dir}/javadoc +excludes= +includes=** +jar.compress=false +javac.classpath= +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=true +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/DuskCommon/nbproject/project.xml b/DuskCommon/nbproject/project.xml new file mode 100644 index 0000000..dabe64d --- /dev/null +++ b/DuskCommon/nbproject/project.xml @@ -0,0 +1,15 @@ + + + org.netbeans.modules.java.j2seproject + + + DuskCommon + + + + + + + + + diff --git a/DuskCommon/src/duskz/protocol/MessageType.java b/DuskCommon/src/duskz/protocol/MessageType.java new file mode 100644 index 0000000..fee4a92 --- /dev/null +++ b/DuskCommon/src/duskz/protocol/MessageType.java @@ -0,0 +1,125 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.protocol; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * Type of message sent from server. + * + * @author notzed + */ +public enum MessageType { + + // 0 + Quit, + UpdateImages, + UpdateLocMap { + + @Override + public ServerMessage decode(DataInputStream instream) throws IOException { + return ServerMessage.MapMessage.decode(instream); + } + }, + Chat, + AddEntity, + // 5 + UpdateStats, + UpdateItems, + UpdateEquipment, + UpdateInfo, + Halt, + // 10 + UpdateActions, + LoadMusic, + PlayMusic, + Ping, + Proceed, + // 15 + PlaySound, + RemoveEntity, + UpdateMerchant, + EditText, + ResizeMap, + // 20 + ViewText, + ExitMerchant, + UpdateSell, + ColourChat, + MoveNorth, + // 25 + MoveSouth, + MoveWest, + MoveEast, + UpdateRange, + SetFlag, + // 30 + ClearFlags, + StartBattle, + UpdateBattle, + LogBattle, + // Above list is compatible with original dusk, new ones follow + // A bunch of login/setup related stuff + /** + * Choose race, response is + * code header + * race 0 + * race 1 + * ... + * . + */ + ChooseRace, + /** + * Damage (or healing) to an entity. + * Encoded as: + * targetID + * +-damage + * newhp + * totalhp + * fromID + * how + * . + */ + HitEntity,; + + public static MessageType fromServer(int v) { + return values()[v]; + } + + public char code() { + return (char) ordinal(); + } + + /** + * Not implemented yet + * @param instream + * @return + * @throws IOException + * @deprecated + */ + @Deprecated + public ServerMessage decode(DataInputStream instream) throws IOException { + return null; + } +} diff --git a/DuskCommon/src/duskz/protocol/ServerMessage.java b/DuskCommon/src/duskz/protocol/ServerMessage.java new file mode 100644 index 0000000..a133fd2 --- /dev/null +++ b/DuskCommon/src/duskz/protocol/ServerMessage.java @@ -0,0 +1,330 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.protocol; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Message from server to client + * + * @author notzed + */ +public abstract class ServerMessage { + + final MessageType type; + + public ServerMessage(MessageType type) { + this.type = type; + } + + public abstract void send(DataOutputStream ostream) throws IOException; + + public static ServerMessage stringMessage(String s) { + return new StringMessage(s); + } + + public static ServerMessage stringMessage(MessageType type, String data) { + return new StringMessage(type, data); + } + + public static ServerMessage mapMessage(int locx, int locy, short[] map) { + return new MapMessage(locx, locy, map); + } + + public static ServerMessage fromServer(DataInputStream instream) throws IOException { + int typec = instream.readByte(); + + MessageType type = MessageType.values()[typec]; + // TODO: more protocol, length? how? + + return type.decode(instream); + } + + public static class StringMessage extends ServerMessage { + + final String data; + + public StringMessage(String data) { + super(null); + this.data = data; + } + + private StringMessage(MessageType type, String data) { + super(type); + this.data = data; + } + + @Override + public void send(DataOutputStream ostream) throws IOException { + if (type != null) + ostream.writeByte(type.code()); + ostream.writeBytes(data); + } + + @Override + public String toString() { + return data; + } + } + + public static class MapMessage extends ServerMessage { + + public int x; + public int y; + public short[] map; + + public MapMessage(int locx, int locy, short[] map) { + super(MessageType.UpdateLocMap); + this.x = locx; + this.y = locy; + this.map = map; + } + + public static MapMessage decode(DataInputStream instream) throws IOException { + int x = instream.readShort(); + int y = instream.readShort(); + int length = instream.readInt(); + short[] map = new short[length]; + for (int i = 0; i < length; i++) { + map[i] = instream.readShort(); + } + return new MapMessage(x, y, map); + } + + @Override + public void send(DataOutputStream ostream) throws IOException { + ostream.writeByte(type.code()); + ostream.writeShort(x); + ostream.writeShort(y); + ostream.writeInt(map.length); + for (int i = 0; i < map.length; i++) + ostream.writeShort(map[i]); + } + } + + public static class EntityMessage extends ServerMessage { + + public long id; + public String name; + public byte entityType; + public short x; + public short y; + public short image; + public short imageStep; + + public EntityMessage(MessageType type, long id, String name, byte entityType, short x, short y, short image, short imageStep) { + super(type); + this.id = id; + this.name = name; + this.entityType = entityType; + this.x = x; + this.y = y; + this.image = image; + this.imageStep = imageStep; + + System.out.printf("create entity message: %d '%s' %d %dx%d image %d %d\n", + id, name, entityType, x, y, image, imageStep); + } + + public static EntityMessage decode(MessageType type, DataInputStream instream) throws IOException { + long id = instream.readLong(); + String name = instream.readUTF(); + byte entityType = instream.readByte(); + short x = instream.readShort(); + short y = instream.readShort(); + short image = instream.readShort(); + short imageStep = instream.readShort(); + + return new EntityMessage(type, id, name, entityType, x, y, image, imageStep); + } + + @Override + public void send(DataOutputStream ostream) throws IOException { + ostream.writeByte(type.code()); + ostream.writeLong(id); + ostream.writeUTF(name); + ostream.writeByte(entityType); + ostream.writeShort(x); + ostream.writeShort(y); + ostream.writeShort(image); + ostream.writeShort(imageStep); + } + } + + /** + * Main player stats + */ + public static class StatsMessage extends ServerMessage { + + public long cash; + public int exp; + public int stre, strebonus; + public int inte, intebonus; + public int dext, dextbonus; + public int cons, consbonus; + public int wisd, wisdbonus; + public int damm, dammbonus; + public int ac, acbonus; + + public StatsMessage(MessageType type, long cash, int exp, int stre, int strebonus, + int inte, int intebonus, int dext, int dextbonus, int cons, int consbonus, + int wisd, int wisdbonus, int damm, int dammbonus, int ac, int acbonus) { + super(type); + this.cash = cash; + this.exp = exp; + this.stre = stre; + this.strebonus = strebonus; + this.inte = inte; + this.intebonus = intebonus; + this.dext = dext; + this.dextbonus = dextbonus; + this.cons = cons; + this.consbonus = consbonus; + this.wisd = wisd; + this.wisdbonus = wisdbonus; + this.damm = damm; + this.dammbonus = dammbonus; + this.ac = ac; + this.acbonus = acbonus; + } + + public static StatsMessage decode(MessageType type, DataInputStream instream) throws IOException { + long cash = instream.readLong(); + int exp = instream.readInt(); + int stre = instream.readInt(); + int strebonus = instream.readInt(); + int inte = instream.readInt(); + int intebonus = instream.readInt(); + int dext = instream.readInt(); + int dextbonus = instream.readInt(); + int cons = instream.readInt(); + int consbonus = instream.readInt(); + int wisd = instream.readInt(); + int wisdbonus = instream.readInt(); + int damm = instream.readInt(); + int dammbonus = instream.readInt(); + int ac = instream.readInt(); + int acbonus = instream.readInt(); + + return new StatsMessage(type, cash, exp, + stre, strebonus, + inte, intebonus, + dext, dextbonus, + cons, consbonus, + wisd, wisdbonus, + damm, dammbonus, + ac, acbonus); + } + + @Override + public void send(DataOutputStream ostream) throws IOException { + ostream.writeByte(type.code()); + ostream.writeLong(cash); + ostream.writeInt(exp); + ostream.writeInt(stre); + ostream.writeInt(strebonus); + ostream.writeInt(inte); + ostream.writeInt(intebonus); + ostream.writeInt(dext); + ostream.writeInt(dextbonus); + ostream.writeInt(cons); + ostream.writeInt(consbonus); + ostream.writeInt(wisd); + ostream.writeInt(wisdbonus); + ostream.writeInt(damm); + ostream.writeInt(dammbonus); + ostream.writeInt(ac); + ostream.writeInt(acbonus); + } + } + + /** + * Other player stats? + * All together or separate? + * UpdateSkills + * UpdateConditions? + */ + /* + strResult += "-Affected by-\n"; + for (Condition cond : conditions) { + if (cond.display) { + strResult += cond.name + "\n"; + } + } + strResult += "-Skills-\n"; + for (Ability skill : skillMap.values()) { + strResult += skill.name + ": " + skill.getAbility() + "\n"; + } + strResult += "-Spells-\n"; + for (Ability spell : spellMap.values()) { + grpStore = game.getSpellGroup(spell.name); + if (grpStore != null) { + strResult += spell.name + ": " + spell.getAbility() + "\n"; + strResult += grpStore.spellList(spell.getAbility()); + } + } + if (master != null) { + strResult += "\nFollowing: " + master.name + "\n"; + }*/ + public static class ItemsMessage extends ServerMessage { + + List items; + + public ItemsMessage(MessageType type, List items) { + super(type); + this.items = items; + } + + public static ItemsMessage decode(MessageType type, DataInputStream instream) throws IOException { + List items = new ArrayList<>(); + + int count = instream.readShort(); + for (int i = 0; i < count; i++) { + TransactionItem item = new TransactionItem(); + + item.name = instream.readUTF(); + item.count = instream.readInt(); + item.cost = instream.readInt(); + item.units = instream.readUTF(); + } + + return new ItemsMessage(type, items); + } + + @Override + public void send(DataOutputStream ostream) throws IOException { + ostream.writeByte(type.code()); + ostream.writeShort(items.size()); + for (TransactionItem item : items) { + ostream.writeUTF(item.getName()); + ostream.writeInt(item.getCount()); + ostream.writeInt(item.getCost()); + ostream.writeUTF(item.getUnits()); + } + } + } +} diff --git a/DuskCommon/src/duskz/protocol/TransactionItem.java b/DuskCommon/src/duskz/protocol/TransactionItem.java new file mode 100644 index 0000000..cead913 --- /dev/null +++ b/DuskCommon/src/duskz/protocol/TransactionItem.java @@ -0,0 +1,72 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.protocol; + +/** + * Represents a transaction item (buy/sell) for server protocol. + * @author notzed + */ +public class TransactionItem implements Comparable{ + + public String name; + public int count; + public int cost; + public String units; + + public TransactionItem() { + } + + + public TransactionItem(String name, int count, int cost, String units) { + this.name = name; + this.count = count; + this.cost = cost; + this.units = units; + } + + public String getName() { + return name; + } + + public int getCount() { + return count; + } + + public int getCost() { + return cost; + } + + public String getUnits() { + return units; + } + + @Override + public String toString() { + return cost + ") " + name + "[" + count + "]"; + } + + @Override + public int compareTo(TransactionItem t) { + return name.compareTo(t.name); + } +} diff --git a/DuskCommon/src/duskz/protocol/Wearing.java b/DuskCommon/src/duskz/protocol/Wearing.java new file mode 100644 index 0000000..ccd254e --- /dev/null +++ b/DuskCommon/src/duskz/protocol/Wearing.java @@ -0,0 +1,50 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.protocol; + +/** + * Constants for wearing locations. + * @author notzed + */ +public interface Wearing { + + public static final int WIELD = 0; + public static final int ARMS = 1; + public static final int LEGS = 2; + public static final int TORSO = 3; + public static final int WAIST = 4; + public static final int NECK = 5; + public static final int SKULL = 6; + public static final int EYES = 7; + public static final int HANDS = 8; + public static final int WEARING_COUNT = 9; + public static final String[] titles = { + "Wielded", "Arms", "Legs", "Torso", "Waist", "Neck", "Skull", "Eyes", "Hands" + }; + /** + * Field names as on the server + */ + public static final String[] names = { + "wielded", "arms", "legs", "torso", "waist", "neck", "skull", "eyes", "hands" + }; +} diff --git a/DuskCommon/src/duskz/util/Debug.java b/DuskCommon/src/duskz/util/Debug.java new file mode 100644 index 0000000..1bacb85 --- /dev/null +++ b/DuskCommon/src/duskz/util/Debug.java @@ -0,0 +1,42 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.util; + +/** + * Debugging utilities. + * @author notzed + */ +public class Debug { + + /** + * Print a stack trace of the current position to stderr. + */ + public static void stackTrace() { + try { + throw new Exception(); + } catch (Exception e) { + System.out.println("The following stacktrace is only for debugging and is not an error"); + e.printStackTrace(); + } + } +} diff --git a/DuskCommon/src/duskz/util/Maths.java b/DuskCommon/src/duskz/util/Maths.java new file mode 100644 index 0000000..9bb6a3e --- /dev/null +++ b/DuskCommon/src/duskz/util/Maths.java @@ -0,0 +1,45 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.util; + +/** + * Some maths utilities. + * + * @author notzed + */ +public class Maths { + + /** + * Clamp an integer to (l,u) inclusive. + * + * @param v + * @param l + * @param u + * @return + */ + public static int clamp(int v, int l, int u) { + v = Math.max(v, l); + return Math.min(v, u); + } +} diff --git a/DuskServer/COPYING b/DuskServer/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/DuskServer/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/DuskServer/README b/DuskServer/README new file mode 100644 index 0000000..658caf2 --- /dev/null +++ b/DuskServer/README @@ -0,0 +1,44 @@ + +README +------ +This is the server implementation of DuskZ. A client is required +to access the game. + +It is a fork and major overhaul of the Dusk 2.7.3 source code, released +circa 2000. + +This is currently in an alpha state and in very active development. + + ... to be completed ... + +INSTALLATION +------------ + ... to be completed ... + +RUNNING +------- + ... to be completed ... + +LICENSE +------- + DuskZ is free software, see COPYING for your rights. + + Some files are under other compatible licenses. + + Copyright (C) 2000 Tom Weingarten + Copyright (C) 2013 Michael Zucchi + + DuskZ is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + DuskZ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DuskZ; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + \ No newline at end of file diff --git a/DuskServer/build.xml b/DuskServer/build.xml new file mode 100644 index 0000000..c74d44f --- /dev/null +++ b/DuskServer/build.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + Builds, tests, and runs the project DuskServer. + + + diff --git a/DuskServer/manifest.mf b/DuskServer/manifest.mf new file mode 100644 index 0000000..328e8e5 --- /dev/null +++ b/DuskServer/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/DuskServer/nbproject/build-impl.xml b/DuskServer/nbproject/build-impl.xml new file mode 100644 index 0000000..d26c819 --- /dev/null +++ b/DuskServer/nbproject/build-impl.xml @@ -0,0 +1,1425 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DuskServer/nbproject/genfiles.properties b/DuskServer/nbproject/genfiles.properties new file mode 100644 index 0000000..0cb0205 --- /dev/null +++ b/DuskServer/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=d4396c02 +build.xml.script.CRC32=b70baa11 +build.xml.stylesheet.CRC32=28e38971@1.56.1.46 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=d4396c02 +nbproject/build-impl.xml.script.CRC32=6bdc7120 +nbproject/build-impl.xml.stylesheet.CRC32=c6d2a60f@1.56.1.46 diff --git a/DuskServer/nbproject/project.properties b/DuskServer/nbproject/project.properties new file mode 100644 index 0000000..347a74d --- /dev/null +++ b/DuskServer/nbproject/project.properties @@ -0,0 +1,77 @@ +application.title=DuskServer +application.vendor=Michael Zucchi +endorsed.classpath= +project.license=dusk +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/DuskServer.jar +dist.javadoc.dir=${dist.dir}/javadoc +excludes= +includes=** +jar.compress=false +javac.classpath=\ + ${reference.DuskCommon.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=duskz.server.DuskServer +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +project.DuskCommon=../DuskCommon +reference.DuskCommon.jar=${project.DuskCommon}/dist/DuskCommon.jar +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/DuskServer/nbproject/project.xml b/DuskServer/nbproject/project.xml new file mode 100644 index 0000000..030b1fb --- /dev/null +++ b/DuskServer/nbproject/project.xml @@ -0,0 +1,25 @@ + + + org.netbeans.modules.java.j2seproject + + + DuskServer + + + + + + + + + + DuskCommon + jar + + jar + clean + jar + + + + diff --git a/DuskServer/src/duskz/io/Tiled.java b/DuskServer/src/duskz/io/Tiled.java new file mode 100644 index 0000000..b8aeda1 --- /dev/null +++ b/DuskServer/src/duskz/io/Tiled.java @@ -0,0 +1,553 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.io; + +import duskz.server.entity.TileMap; +import duskz.io.tiled.Data; +import duskz.io.tiled.Image; +import duskz.io.tiled.Layer; +import duskz.io.tiled.Map; +import duskz.io.tiled.Property; +import duskz.io.tiled.Tile; +import duskz.io.tiled.Tileset; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.io.StringReader; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import javax.imageio.ImageIO; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.MemoryCacheImageInputStream; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; + +/** + * Tiled loader/converter. This is only experimental at this stage. + * + * @author notzed + */ +public class Tiled { + + static byte[] map64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes(Charset.forName("US-ASCII")); + static byte[] unmap64 = new byte[256]; + + static { + for (int i = 0; i < 256; i++) + unmap64[i] = -1; + for (int i = 0; i < 64; i++) { + unmap64[map64[i]] = (byte) i; + } + } + // gzip and base64 result + + static String encodeArray(byte[] data) throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + GZIPOutputStream gz = new GZIPOutputStream(os); + + gz.write(data); + gz.close(); + return encodeBytes(os.toByteArray()); + } + + static InputStream decodeArray(String s) throws IOException { + ByteArrayInputStream is = new ByteArrayInputStream(decodeBytes(s)); + GZIPInputStream gz = new GZIPInputStream(is); + + return gz; + } + + static int getCode(StringReader sr) throws IOException { + int v; + + do { + v = sr.read(); + if (v == -1) + return -1; + if (v == '=') + return -1; + v = unmap64[v]; + } while (v == -1); + return v; + } + + static byte[] decodeBytes(String s) throws IOException { + ByteBuffer dec = ByteBuffer.allocate(s.length()); + StringReader sr = new StringReader(s); + int a, b, c, d; + do { + a = getCode(sr); + b = getCode(sr); + c = getCode(sr); + d = getCode(sr); + + if (a == -1) { + // Truncated input + } else if (b == -1) { + // Truncated input + } else if (c == -1) { + // one encoded value + dec.put((byte) ((a << 2) | (b >> 4))); + } else if (d == -1) { + // 2 encoded valus + dec.put((byte) ((a << 2) | (b >> 4))); + dec.put((byte) ((b << 4) | (c >> 2))); + } else { + dec.put((byte) ((a << 2) | (b >> 4))); + dec.put((byte) ((b << 4) | (c >> 2))); + dec.put((byte) ((c << 6) | d)); + } + } while (d != -1); + + return Arrays.copyOf(dec.array(), dec.position()); + } + + static String encodeBytes(byte[] bytes) { + // Convert to base64 + ByteBuffer enc = ByteBuffer.allocate(bytes.length * 4); + int i = 0; + int line = 0; + while (i < bytes.length) { + int a = bytes[i++]; + int b = 0; + int c = 0; + int count = 2; + + if (i < bytes.length) { + b = bytes[i++]; + count = 3; + } + if (i < bytes.length) { + c = bytes[i++]; + count = 4; + } + + enc.put(map64[(a >> 2) & 63]); + enc.put(map64[((a << 4) & 0x30) | ((b >> 4) & 15)]); + if (count > 2) + enc.put(map64[((b << 2) & 0x3c) | ((c >> 6) & 3)]); + else + enc.put((byte) '='); + if (count > 3) + enc.put(map64[c & 63]); + else + enc.put((byte) '='); + + line += 4; + if (line >= 72) { + enc.put((byte) 10); + line = 0; + } + } + return new String(enc.array(), 0, enc.position(), Charset.forName("us-ascii")); + } + + static String encodeImage(BufferedImage image) throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + ImageIO.write(image, "png", os); + os.close(); + return encodeBytes(os.toByteArray()); + } + + static BufferedImage decodeImage(String enc) throws IOException { + byte[] data = decodeBytes(enc); + InputStream is = new ByteArrayInputStream(data); + + return ImageIO.read(is); + } + + static void testexport() throws IOException { + File path = new File("/home/notzed/src/DuskRPG/DuskFiles/Dusk2.7.3/shortmap"); + + TileMap map = TileMap.loadMap(path, TileMap.FORMAT_SHORT); + int width = map.getCols(); + int height = map.getRows(); + int len = width * height; + + + System.out.println("Map size: " + width + "," + height); + + duskz.io.tiled.Map tmap = new duskz.io.tiled.Map(); + + tmap.setWidth(width); + tmap.setHeight(height); + tmap.setOrientation("orthogonal"); + tmap.setVersion("1.0"); + tmap.setTilewidth(32); + tmap.setTileheight(32); + + Tileset tset = new Tileset(); + tmap.getTileset().add(tset); + + tset.setTileheight(32); + tset.setTilewidth(32); + tset.setFirstgid(0); + tset.setName("dusk-map"); + + // Add tile set + BufferedImage tiles = ImageIO.read(new File("/home/notzed/src/DuskRPG/DuskFiles/Dusk2.7.3/www/rc/somedusk/images/map.png")); + int tileWidth = 32; + int tileHeight = 32; + int imageWidth = tiles.getWidth(); + for (int x = 0; x * tileWidth < imageWidth; x++) { + BufferedImage tile = tiles.getSubimage(x * tileWidth, 0, tileWidth, tileHeight); + + Image timage = new Image(); + Data idata = new Data(); + + idata.setEncoding("base64"); + idata.setvalue(encodeImage(tile)); + timage.setData(idata); + timage.setFormat("png"); + + Tile ttile = new Tile(); + ttile.setId(x); + ttile.setImage(timage); + + tset.getTile().add(ttile); + } + + // Add layer + Layer layer = new Layer(); + + tmap.getLayerOrObjectgroup().add(layer); + + layer.setName("base"); + layer.setVisible(1); + layer.setOpacity(1); + layer.setWidth(map.getCols()); + layer.setHeight(map.getRows()); + + Data d = new Data(); + + layer.setData(d); + + d.setCompression("gzip"); + d.setEncoding("base64"); + + // First convert to ints + ByteBuffer bb = ByteBuffer.allocate(len * 4).order(ByteOrder.LITTLE_ENDIAN); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + bb.putInt(map.getTile(x, y)); + } + } + bb.rewind(); + + d.setvalue(encodeArray(bb.array())); + //d.setvalue("dummy"); + + try { + JAXBContext jc = JAXBContext.newInstance(Map.class); + //Unmarshaller u = jc.createUnmarshaller(); + //Object element = u.unmarshal(new File("foo.xml")); + Marshaller m = jc.createMarshaller(); + + m.setProperty("jaxb.formatted.output", true); + + + m.marshal(tmap, new File("/home/notzed/tiled-test.tmx")); + + } catch (JAXBException ex) { + Logger.getLogger(Tiled.class.getName()).log(Level.SEVERE, null, ex); + } + System.out.println("done"); + + } + + static class TileInfo { + + int id; + // Script names + String move; + String see; + String action; + } + + static void testimport(File src, File dst) throws IOException { + try { + JAXBContext jc = JAXBContext.newInstance(Map.class); + Unmarshaller u = jc.createUnmarshaller(); + Map tmap = (Map) u.unmarshal(src); + + System.out.println("Loaded map size: " + tmap.getWidth() + "x" + tmap.getHeight()); + + Manifest man = new Manifest(); + + List tileInfo = new ArrayList<>(); + int tid = 0; + + // Create manifest entires for tileset + if (true) { + int setid = 0; + + Attributes a = new Attributes(); + + for (Tileset ts : tmap.getTileset()) { + int gid0 = ts.getFirstgid(); + + if (ts.getSource() != null) { + URI uri = src.toURI().resolve(ts.getSource()); + + ts = (Tileset) u.unmarshal(new File(uri)); + } + if (!ts.getTile().isEmpty()) { + Tile tt = ts.getTile().get(0); + BufferedImage image = decodeImage(tt.getImage().getData().getvalue()); + + int twidth = image.getWidth(); + int theight = image.getHeight(); + + a.putValue(setid + "_name", ts.getName()); + a.putValue(setid + "_gid", String.valueOf(gid0)); + a.putValue(setid + "_count", String.valueOf(ts.getTile().size())); + a.putValue(setid + "_source", "tiles/tiles_" + gid0 + ".png"); + a.putValue(setid + "_width", String.valueOf(twidth)); + a.putValue(setid + "_height", String.valueOf(theight)); + setid++; + + tid = 0; + // Add itle info + for (Tile ttile : ts.getTile()) { + TileInfo ti = new TileInfo(); + + // assume tid.id == tile id? + if (tid != ttile.getId()) { + System.out.println("shit tile id mismatch: " + tid + "!= " + ttile.getId()); + } + ti.id = gid0 + tid; + tid++; + + if (ttile.getProperties() != null) { + for (Property p : ttile.getProperties().getProperty()) { + switch (p.getName()) { + case "move": + ti.move = p.getValue(); + break; + case "see": + ti.see = p.getValue(); + break; + case "action": + ti.action = p.getValue(); + break; + } + } + } + if (ti.move != null && ti.see == null) { + ti.see = ti.move; + } + tileInfo.add(ti); + } + + } + + } + a.putValue("count", String.valueOf(setid)); + man.getEntries().put("tilesets", a); + } + + JarOutputStream jos = new JarOutputStream(new FileOutputStream(dst), man); + { + String[] dirs = {"maps/", "tiles/"}; + for (String d : dirs) { + JarEntry je = new JarEntry(d); + jos.putNextEntry(je); + jos.closeEntry(); + } + } + + TileMap map = new TileMap(tmap.getWidth(), tmap.getHeight()); + + int lid = 0; + + for (Object o : tmap.getLayerOrObjectgroup()) { + if (o instanceof Layer) { + Layer l = (Layer) o; + + // FIXME: remap map? + //l.getData().getvalue(); + + // encoding blah blah + + InputStream is = decodeArray(l.getData().getvalue()); + ImageInputStream dis = new MemoryCacheImageInputStream(is); + + dis.setByteOrder(ByteOrder.LITTLE_ENDIAN); + + // Convert to shorts + JarEntry je = new JarEntry("maps/0-" + lid + "-" + l.getName()); + jos.putNextEntry(je); + + DataOutputStream dos = new DataOutputStream(jos); + dos.writeInt(l.getWidth()); + dos.writeInt(l.getHeight()); + for (int y = 0; y < l.getHeight(); y++) { + for (int x = 0; x < l.getWidth(); x++) { + int t = dis.readInt(); + dos.writeShort(t); + //System.out.printf("%3d\n", t); + } + } + dos.flush(); + //dos.close(); + + jos.closeEntry(); + } + } + + // Add tile script names + { + JarEntry je = new JarEntry("tiles/scripts"); + jos.putNextEntry(je); + PrintStream ps = new PrintStream(jos); + for (TileInfo ti : tileInfo) { + if (ti.move != null && !ti.move.equals("false")) { + ps.format("move.%d=%s\n", ti.id, ti.move); + } + if (ti.see != null && !ti.see.equals("false")) { + ps.format("see.%d=%s\n", ti.id, ti.see); + } + if (ti.action != null) { + ps.format("action.%d=%s\n", ti.id, ti.action); + } + } + ps.flush(); + jos.closeEntry(); + } + + // Build tileset manifest - nope in manifest + if (false) { + int setid = 0; + JarEntry je = new JarEntry("tiles/manifest"); + jos.putNextEntry(je); + PrintStream ps = new PrintStream(jos); + for (Tileset ts : tmap.getTileset()) { + int gid0 = ts.getFirstgid(); + + if (ts.getSource() != null) { + URI uri = src.toURI().resolve(ts.getSource()); + + ts = (Tileset) u.unmarshal(new File(uri)); + } + if (!ts.getTile().isEmpty()) { + Tile tt = ts.getTile().get(0); + BufferedImage image = decodeImage(tt.getImage().getData().getvalue()); + + int twidth = image.getWidth(); + int theight = image.getHeight(); + + ps.format("tiles.%d.name=%s\n", setid, ts.getName()); + ps.format("tiles.%s.width=%d\n", setid, twidth); + ps.format("tiles.%s.height=%d\n", setid, theight); + ps.format("tiles.%s.source=tiles-%d.png\n", setid, gid0); + ps.format("tiles.%s.firstGid=%d\n", setid, gid0); + setid++; + } + + } + ps.format("tiles.count=%d\n", setid); + ps.flush(); + jos.closeEntry(); + } + + for (Tileset ts : tmap.getTileset()) { + int gid0 = ts.getFirstgid(); + + if (ts.getSource() != null) { + URI uri = src.toURI().resolve(ts.getSource()); + + ts = (Tileset) u.unmarshal(new File(uri)); + } + + + int twidth = 0, theight = 0; + BufferedImage tmp = null; + Graphics2D gg = null; + + System.out.println("found tileset: " + ts.getName()); + + int ntiles = ts.getTile().size(); + + int i = 0; + for (Tile tt : ts.getTile()) { + BufferedImage image = decodeImage(tt.getImage().getData().getvalue()); + + if (tmp == null) { + twidth = image.getWidth(); + theight = image.getHeight(); + tmp = new BufferedImage(ntiles * twidth, theight, BufferedImage.TYPE_INT_ARGB); + gg = tmp.createGraphics(); + } else if (image.getWidth() != twidth || image.getHeight() != theight) { + System.out.println("Image tiles differ in size\n"); + } + + gg.drawImage(image, (i++) * twidth, 0, null); + } + + if (tmp != null) { + //ImageIO.write(tmp, "png", new File("/home/notzed/tiled-tiles-" + twidth + "x" + theight + ".png")); + + JarEntry ze = new JarEntry("tiles/tiles_" + gid0 + ".png"); + jos.putNextEntry(ze); + ImageIO.write(tmp, "png", jos); + jos.closeEntry(); + } + } + jos.close(); + + } catch (JAXBException ex) { + Logger.getLogger(Tiled.class.getName()).log(Level.SEVERE, null, ex); + } + } + // test load/save itled format map + + public static void main(String[] args) throws IOException { + testimport(new File("/home/notzed/house.tmx"), + new File("/home/notzed/house.jar")); + //testexport(); + } +} diff --git a/DuskServer/src/duskz/io/tiled/Data.java b/DuskServer/src/duskz/io/tiled/Data.java new file mode 100644 index 0000000..f6f9b8f --- /dev/null +++ b/DuskServer/src/duskz/io/tiled/Data.java @@ -0,0 +1,112 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2013.02.27 at 01:07:39 PM CST +// + + +package duskz.io.tiled; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; +import javax.xml.bind.annotation.adapters.NormalizedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + + +/** + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "data") +public class Data { + + @XmlAttribute(name = "encoding") + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String encoding; + @XmlAttribute(name = "compression") + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String compression; + @XmlValue + protected String value; + + /** + * Gets the value of the encoding property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getEncoding() { + return encoding; + } + + /** + * Sets the value of the encoding property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setEncoding(String value) { + this.encoding = value; + } + + /** + * Gets the value of the compression property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getCompression() { + return compression; + } + + /** + * Sets the value of the compression property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setCompression(String value) { + this.compression = value; + } + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getvalue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setvalue(String value) { + this.value = value; + } + +} diff --git a/DuskServer/src/duskz/io/tiled/Image.java b/DuskServer/src/duskz/io/tiled/Image.java new file mode 100644 index 0000000..709c33e --- /dev/null +++ b/DuskServer/src/duskz/io/tiled/Image.java @@ -0,0 +1,162 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2013.02.27 at 01:07:39 PM CST +// + + +package duskz.io.tiled; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.NormalizedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + + +/** + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "data" +}) +@XmlRootElement(name = "image") +public class Image { + + @XmlAttribute(name = "format") + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String format; + @XmlAttribute(name = "id") + protected String id; + @XmlAttribute(name = "source") + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String source; + @XmlAttribute(name = "trans") + protected String trans; + protected Data data; + + /** + * Gets the value of the format property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getFormat() { + return format; + } + + /** + * Sets the value of the format property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setFormat(String value) { + this.format = value; + } + + /** + * Gets the value of the id property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getId() { + return id; + } + + /** + * Sets the value of the id property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setId(String value) { + this.id = value; + } + + /** + * Gets the value of the source property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getSource() { + return source; + } + + /** + * Sets the value of the source property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setSource(String value) { + this.source = value; + } + + /** + * Gets the value of the trans property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTrans() { + return trans; + } + + /** + * Sets the value of the trans property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTrans(String value) { + this.trans = value; + } + + /** + * Gets the value of the data property. + * + * @return + * possible object is + * {@link Data } + * + */ + public Data getData() { + return data; + } + + /** + * Sets the value of the data property. + * + * @param value + * allowed object is + * {@link Data } + * + */ + public void setData(Data value) { + this.data = value; + } + +} diff --git a/DuskServer/src/duskz/io/tiled/Layer.java b/DuskServer/src/duskz/io/tiled/Layer.java new file mode 100644 index 0000000..1d52347 --- /dev/null +++ b/DuskServer/src/duskz/io/tiled/Layer.java @@ -0,0 +1,268 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2013.02.27 at 01:07:39 PM CST +// + + +package duskz.io.tiled; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.CollapsedStringAdapter; +import javax.xml.bind.annotation.adapters.NormalizedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + + +/** + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "properties", + "data" +}) +@XmlRootElement(name = "layer") +public class Layer { + + @XmlAttribute(name = "name", required = true) + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String name; + @XmlAttribute(name = "width", required = true) + protected int width; + @XmlAttribute(name = "height", required = true) + protected int height; + @XmlAttribute(name = "x") + protected int x; + @XmlAttribute(name = "y") + protected int y; + @XmlAttribute(name = "opacity") + protected float opacity; + @XmlAttribute(name = "visible") + protected int visible; + protected Properties properties; + @XmlElement(required = true) + protected Data data; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the width property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getWidth() { + return width; + } + + /** + * Sets the value of the width property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setWidth(int value) { + this.width = value; + } + + /** + * Gets the value of the height property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getHeight() { + return height; + } + + /** + * Sets the value of the height property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setHeight(int value) { + this.height = value; + } + + /** + * Gets the value of the x property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getX() { + return x; + } + + /** + * Sets the value of the x property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setX(int value) { + this.x = value; + } + + /** + * Gets the value of the y property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getY() { + return y; + } + + /** + * Sets the value of the y property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setY(int value) { + this.y = value; + } + + /** + * Gets the value of the opacity property. + * + * @return + * possible object is + * {@link String } + * + */ + public float getOpacity() { + return opacity; + } + + /** + * Sets the value of the opacity property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOpacity(float value) { + this.opacity = value; + } + + /** + * Gets the value of the visible property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getVisible() { + return visible; + } + + /** + * Sets the value of the visible property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setVisible(int value) { + this.visible = value; + } + + /** + * Gets the value of the properties property. + * + * @return + * possible object is + * {@link Properties } + * + */ + public Properties getProperties() { + return properties; + } + + /** + * Sets the value of the properties property. + * + * @param value + * allowed object is + * {@link Properties } + * + */ + public void setProperties(Properties value) { + this.properties = value; + } + + /** + * Gets the value of the data property. + * + * @return + * possible object is + * {@link Data } + * + */ + public Data getData() { + return data; + } + + /** + * Sets the value of the data property. + * + * @param value + * allowed object is + * {@link Data } + * + */ + public void setData(Data value) { + this.data = value; + } + +} diff --git a/DuskServer/src/duskz/io/tiled/Map.java b/DuskServer/src/duskz/io/tiled/Map.java new file mode 100644 index 0000000..12a6585 --- /dev/null +++ b/DuskServer/src/duskz/io/tiled/Map.java @@ -0,0 +1,367 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2013.02.27 at 01:07:39 PM CST +// + + +package duskz.io.tiled; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElements; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.CollapsedStringAdapter; +import javax.xml.bind.annotation.adapters.NormalizedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + + +/** + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "properties", + "tileset", + "layerOrObjectgroup" +}) +@XmlRootElement(name = "map") +public class Map { + + @XmlAttribute(name = "xmlns") + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String xmlns; + @XmlAttribute(name = "xmlns:xsi") + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String xmlnsXsi; + @XmlAttribute(name = "xsi:schemaLocation") + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String xsiSchemaLocation; + @XmlAttribute(name = "version", required = true) + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String version; + @XmlAttribute(name = "orientation", required = true) + @XmlJavaTypeAdapter(CollapsedStringAdapter.class) + protected String orientation; + @XmlAttribute(name = "width", required = true) + protected int width; + @XmlAttribute(name = "height", required = true) + protected int height; + @XmlAttribute(name = "tilewidth", required = true) + protected int tilewidth; + @XmlAttribute(name = "tileheight", required = true) + protected int tileheight; + protected Properties properties; + protected List tileset; + @XmlElements({ + @XmlElement(name = "layer", type = Layer.class), + @XmlElement(name = "objectgroup", type = Objectgroup.class) + }) + protected List layerOrObjectgroup; + + /** + * Gets the value of the xmlns property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getXmlns() { + return xmlns; + } + + /** + * Sets the value of the xmlns property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setXmlns(String value) { + this.xmlns = value; + } + + /** + * Gets the value of the xmlnsXsi property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getXmlnsXsi() { + return xmlnsXsi; + } + + /** + * Sets the value of the xmlnsXsi property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setXmlnsXsi(String value) { + this.xmlnsXsi = value; + } + + /** + * Gets the value of the xsiSchemaLocation property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getXsiSchemaLocation() { + return xsiSchemaLocation; + } + + /** + * Sets the value of the xsiSchemaLocation property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setXsiSchemaLocation(String value) { + this.xsiSchemaLocation = value; + } + + /** + * Gets the value of the version property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getVersion() { + return version; + } + + /** + * Sets the value of the version property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setVersion(String value) { + this.version = value; + } + + /** + * Gets the value of the orientation property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOrientation() { + return orientation; + } + + /** + * Sets the value of the orientation property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOrientation(String value) { + this.orientation = value; + } + + /** + * Gets the value of the width property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getWidth() { + return width; + } + + /** + * Sets the value of the width property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setWidth(int value) { + this.width = value; + } + + /** + * Gets the value of the height property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getHeight() { + return height; + } + + /** + * Sets the value of the height property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setHeight(int value) { + this.height = value; + } + + /** + * Gets the value of the tilewidth property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getTilewidth() { + return tilewidth; + } + + /** + * Sets the value of the tilewidth property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTilewidth(int value) { + this.tilewidth = value; + } + + /** + * Gets the value of the tileheight property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getTileheight() { + return tileheight; + } + + /** + * Sets the value of the tileheight property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTileheight(int value) { + this.tileheight = value; + } + + /** + * Gets the value of the properties property. + * + * @return + * possible object is + * {@link Properties } + * + */ + public Properties getProperties() { + return properties; + } + + /** + * Sets the value of the properties property. + * + * @param value + * allowed object is + * {@link Properties } + * + */ + public void setProperties(Properties value) { + this.properties = value; + } + + /** + * Gets the value of the tileset property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the tileset property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getTileset().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link Tileset } + * + * + */ + public List getTileset() { + if (tileset == null) { + tileset = new ArrayList(); + } + return this.tileset; + } + + /** + * Gets the value of the layerOrObjectgroup property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the layerOrObjectgroup property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getLayerOrObjectgroup().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link Layer } + * {@link Objectgroup } + * + * + */ + public List getLayerOrObjectgroup() { + if (layerOrObjectgroup == null) { + layerOrObjectgroup = new ArrayList(); + } + return this.layerOrObjectgroup; + } + +} diff --git a/DuskServer/src/duskz/io/tiled/Object.java b/DuskServer/src/duskz/io/tiled/Object.java new file mode 100644 index 0000000..fe12a79 --- /dev/null +++ b/DuskServer/src/duskz/io/tiled/Object.java @@ -0,0 +1,240 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2013.02.27 at 01:07:39 PM CST +// + + +package duskz.io.tiled; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.NormalizedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + + +/** + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "properties", + "image" +}) +@XmlRootElement(name = "object") +public class Object { + + @XmlAttribute(name = "name", required = true) + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String name; + @XmlAttribute(name = "type", required = true) + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String type; + @XmlAttribute(name = "x", required = true) + protected int x; + @XmlAttribute(name = "y", required = true) + protected int y; + @XmlAttribute(name = "width") + protected int width; + @XmlAttribute(name = "height") + protected int height; + protected Properties properties; + protected Image image; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the type property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getType() { + return type; + } + + /** + * Sets the value of the type property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setType(String value) { + this.type = value; + } + + /** + * Gets the value of the x property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getX() { + return x; + } + + /** + * Sets the value of the x property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setX(int value) { + this.x = value; + } + + /** + * Gets the value of the y property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getY() { + return y; + } + + /** + * Sets the value of the y property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setY(int value) { + this.y = value; + } + + /** + * Gets the value of the width property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getWidth() { + return width; + } + + /** + * Sets the value of the width property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setWidth(int value) { + this.width = value; + } + + /** + * Gets the value of the height property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getHeight() { + return height; + } + + /** + * Sets the value of the height property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setHeight(int value) { + this.height = value; + } + + /** + * Gets the value of the properties property. + * + * @return + * possible object is + * {@link Properties } + * + */ + public Properties getProperties() { + return properties; + } + + /** + * Sets the value of the properties property. + * + * @param value + * allowed object is + * {@link Properties } + * + */ + public void setProperties(Properties value) { + this.properties = value; + } + + /** + * Gets the value of the image property. + * + * @return + * possible object is + * {@link Image } + * + */ + public Image getImage() { + return image; + } + + /** + * Sets the value of the image property. + * + * @param value + * allowed object is + * {@link Image } + * + */ + public void setImage(Image value) { + this.image = value; + } + +} diff --git a/DuskServer/src/duskz/io/tiled/ObjectFactory.java b/DuskServer/src/duskz/io/tiled/ObjectFactory.java new file mode 100644 index 0000000..1745074 --- /dev/null +++ b/DuskServer/src/duskz/io/tiled/ObjectFactory.java @@ -0,0 +1,119 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2013.02.27 at 01:07:39 PM CST +// + + +package duskz.io.tiled; + +import javax.xml.bind.annotation.XmlRegistry; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the generated package. + *

An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: generated + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link Tileset } + * + */ + public Tileset createTileset() { + return new Tileset(); + } + + /** + * Create an instance of {@link Image } + * + */ + public Image createImage() { + return new Image(); + } + + /** + * Create an instance of {@link Tile } + * + */ + public Tile createTile() { + return new Tile(); + } + + /** + * Create an instance of {@link Properties } + * + */ + public Properties createProperties() { + return new Properties(); + } + + /** + * Create an instance of {@link Objectgroup } + * + */ + public Objectgroup createObjectgroup() { + return new Objectgroup(); + } + + /** + * Create an instance of {@link Object } + * + */ + public Object createObject() { + return new Object(); + } + + /** + * Create an instance of {@link Data } + * + */ + public Data createData() { + return new Data(); + } + + /** + * Create an instance of {@link Map } + * + */ + public Map createMap() { + return new Map(); + } + + /** + * Create an instance of {@link Layer } + * + */ + public Layer createLayer() { + return new Layer(); + } + + /** + * Create an instance of {@link Property } + * + */ + public Property createProperty() { + return new Property(); + } + +} diff --git a/DuskServer/src/duskz/io/tiled/Objectgroup.java b/DuskServer/src/duskz/io/tiled/Objectgroup.java new file mode 100644 index 0000000..06cb7fe --- /dev/null +++ b/DuskServer/src/duskz/io/tiled/Objectgroup.java @@ -0,0 +1,194 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2013.02.27 at 01:07:39 PM CST +// + + +package duskz.io.tiled; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.NormalizedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + + +/** + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "object" +}) +@XmlRootElement(name = "objectgroup") +public class Objectgroup { + + @XmlAttribute(name = "name", required = true) + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String name; + @XmlAttribute(name = "width") + protected int width; + @XmlAttribute(name = "height") + protected int height; + @XmlAttribute(name = "x") + protected int x; + @XmlAttribute(name = "y") + protected int y; + protected List object; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the width property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getWidth() { + return width; + } + + /** + * Sets the value of the width property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setWidth(int value) { + this.width = value; + } + + /** + * Gets the value of the height property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getHeight() { + return height; + } + + /** + * Sets the value of the height property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setHeight(int value) { + this.height = value; + } + + /** + * Gets the value of the x property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getX() { + return x; + } + + /** + * Sets the value of the x property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setX(int value) { + this.x = value; + } + + /** + * Gets the value of the y property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getY() { + return y; + } + + /** + * Sets the value of the y property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setY(int value) { + this.y = value; + } + + /** + * Gets the value of the object property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the object property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getObject().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link Object } + * + * + */ + public List getObject() { + if (object == null) { + object = new ArrayList(); + } + return this.object; + } + +} diff --git a/DuskServer/src/duskz/io/tiled/Properties.java b/DuskServer/src/duskz/io/tiled/Properties.java new file mode 100644 index 0000000..97355d8 --- /dev/null +++ b/DuskServer/src/duskz/io/tiled/Properties.java @@ -0,0 +1,60 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2013.02.27 at 01:07:39 PM CST +// + + +package duskz.io.tiled; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "property" +}) +@XmlRootElement(name = "properties") +public class Properties { + + protected List property; + + /** + * Gets the value of the property property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the property property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getProperty().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link Property } + * + * + */ + public List getProperty() { + if (property == null) { + property = new ArrayList(); + } + return this.property; + } + +} diff --git a/DuskServer/src/duskz/io/tiled/Property.java b/DuskServer/src/duskz/io/tiled/Property.java new file mode 100644 index 0000000..2cd68bb --- /dev/null +++ b/DuskServer/src/duskz/io/tiled/Property.java @@ -0,0 +1,83 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2013.02.27 at 01:07:39 PM CST +// + + +package duskz.io.tiled; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.NormalizedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + + +/** + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "property") +public class Property { + + @XmlAttribute(name = "name", required = true) + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String name; + @XmlAttribute(name = "value", required = true) + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String value; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/DuskServer/src/duskz/io/tiled/Tile.java b/DuskServer/src/duskz/io/tiled/Tile.java new file mode 100644 index 0000000..f474d71 --- /dev/null +++ b/DuskServer/src/duskz/io/tiled/Tile.java @@ -0,0 +1,134 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2013.02.27 at 01:07:39 PM CST +// + + +package duskz.io.tiled; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.NormalizedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + + +/** + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "properties", + "image" +}) +@XmlRootElement(name = "tile") +public class Tile { + + @XmlAttribute(name = "id") + protected int id; + @XmlAttribute(name = "gid") + protected String gid; + protected Properties properties; + protected Image image; + + /** + * Gets the value of the id property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getId() { + return id; + } + + /** + * Sets the value of the id property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setId(int value) { + this.id = value; + } + + /** + * Gets the value of the gid property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getGid() { + return gid; + } + + /** + * Sets the value of the gid property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setGid(String value) { + this.gid = value; + } + + /** + * Gets the value of the properties property. + * + * @return + * possible object is + * {@link Properties } + * + */ + public Properties getProperties() { + return properties; + } + + /** + * Sets the value of the properties property. + * + * @param value + * allowed object is + * {@link Properties } + * + */ + public void setProperties(Properties value) { + this.properties = value; + } + + /** + * Gets the value of the image property. + * + * @return + * possible object is + * {@link Image } + * + */ + public Image getImage() { + return image; + } + + /** + * Sets the value of the image property. + * + * @param value + * allowed object is + * {@link Image } + * + */ + public void setImage(Image value) { + this.image = value; + } + +} diff --git a/DuskServer/src/duskz/io/tiled/Tileset.java b/DuskServer/src/duskz/io/tiled/Tileset.java new file mode 100644 index 0000000..f6afbf6 --- /dev/null +++ b/DuskServer/src/duskz/io/tiled/Tileset.java @@ -0,0 +1,279 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2013.02.27 at 01:07:39 PM CST +// + + +package duskz.io.tiled; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.NormalizedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + + +/** + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "image", + "tile" +}) +@XmlRootElement(name = "tileset") +public class Tileset { + + @XmlAttribute(name = "name") + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String name; + @XmlAttribute(name = "firstgid", required = true) + protected int firstgid; + @XmlAttribute(name = "source") + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String source; + @XmlAttribute(name = "tilewidth") + protected int tilewidth; + @XmlAttribute(name = "tileheight") + protected int tileheight; + @XmlAttribute(name = "spacing") + protected int spacing; + @XmlAttribute(name = "margin") + @XmlJavaTypeAdapter(NormalizedStringAdapter.class) + protected String margin; + protected List image; + protected List tile; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the firstgid property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getFirstgid() { + return firstgid; + } + + /** + * Sets the value of the firstgid property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setFirstgid(int value) { + this.firstgid = value; + } + + /** + * Gets the value of the source property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getSource() { + return source; + } + + /** + * Sets the value of the source property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setSource(String value) { + this.source = value; + } + + /** + * Gets the value of the tilewidth property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getTilewidth() { + return tilewidth; + } + + /** + * Sets the value of the tilewidth property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTilewidth(int value) { + this.tilewidth = value; + } + + /** + * Gets the value of the tileheight property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getTileheight() { + return tileheight; + } + + /** + * Sets the value of the tileheight property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTileheight(int value) { + this.tileheight = value; + } + + /** + * Gets the value of the spacing property. + * + * @return + * possible object is + * {@link String } + * + */ + public int getSpacing() { + return spacing; + } + + /** + * Sets the value of the spacing property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setSpacing(int value) { + this.spacing = value; + } + + /** + * Gets the value of the margin property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getMargin() { + return margin; + } + + /** + * Sets the value of the margin property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setMargin(String value) { + this.margin = value; + } + + /** + * Gets the value of the image property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the image property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getImage().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link Image } + * + * + */ + public List getImage() { + if (image == null) { + image = new ArrayList(); + } + return this.image; + } + + /** + * Gets the value of the tile property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the tile property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getTile().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link Tile } + * + * + */ + public List getTile() { + if (tile == null) { + tile = new ArrayList(); + } + return this.tile; + } + +} diff --git a/DuskServer/src/duskz/proto/Junk.java b/DuskServer/src/duskz/proto/Junk.java new file mode 100644 index 0000000..55a337e --- /dev/null +++ b/DuskServer/src/duskz/proto/Junk.java @@ -0,0 +1,278 @@ +/* + * Jus test code, public domain + */ +package duskz.proto; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.AccessControlContext; +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Permissions; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.security.cert.Certificate; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.script.Bindings; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import javax.script.SimpleScriptContext; + +/** + * This is just a place holder to test stuff in the ide + * + * + * for security test: java -Djava.security.manager -Djava.security.policy=dusk.policy -classpath dist/DuskCommon.jar au.dusk.Junk + * + * @author notzed + */ +public class Junk { + + int MapColumns = 64; + int MapRows = 64; + int viewrange = 7; + int mapsize = 1 + (2 * viewrange); + + public void chatMessage(String inMessage, int intLocX, int intLocY, String strFrom) { + + System.out.printf("look at %d,%d\n", intLocX, intLocY); + + + int mx, y0, my; + mx = 0; + if (intLocX - viewrange < 0) { + mx = -1 * (intLocX - viewrange); + } + y0 = 0; + if (intLocY - viewrange < 0) { + y0 = -1 * (intLocY - viewrange); + } + for (; mx < mapsize; mx++) { + if (intLocX + mx - viewrange < MapColumns) { + for (my = y0; my < mapsize; my++) { + if (intLocY + my - viewrange < MapRows) { + int x = intLocX + mx - viewrange; + int y = intLocY + my - viewrange; + + System.out.printf("visit %d,%d\n", x, y); + } + } + } + } + } + + public static class Thing { + + FileOutputStream fos; + + public Thing() { + try { + fos = new FileOutputStream("foo.txt"); + } catch (FileNotFoundException ex) { + Logger.getLogger(Junk.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public boolean isPet() { + return false; + } + + public boolean hasCondition(String name) { + System.out.println("has trigger " + name); + return true; + } + + public void removeCondition(String name) { + System.out.println("remove trigger " + name); + } + + public void moveTo(int x, int y) { + System.out.println("move to " + x + "," + y); + } + + public void chat(String what) { + try { + //fos = new FileOutputStream("foo.txt"); + fos.write(what.getBytes()); + //fos.close(); + } catch (IOException ex) { + Logger.getLogger(Junk.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + static ScriptEngine engine; + + public static void main(String[] args) throws ScriptException, InterruptedException { + //new Junk().chatMessage(null, 16, 16, null); + //new Junk().chatMessage(null, 2, 2, null); + // create a script engine manager + // evaluate JavaScript code from given file - specified by first argument + + + try (FileOutputStream fos = new FileOutputStream("not-hack-a")) { + fos.write(1); + System.out.println("wrote not-hack-a"); + } catch (IOException x) { + x.printStackTrace(); + } catch (AccessControlException x) { + x.printStackTrace(); + } + + + /* + SecurityManager sm = new SecurityManager() { + @Override + public void checkPermission(Permission perm, Object context) { + System.out.println("check perm ctx " + perm); + super.checkPermission(perm, context); + + } + + @Override + public void checkPermission(Permission perm) { + System.out.println("check perm " + perm); + super.checkPermission(perm); + } + };*/ + //System.setSecurityManager(sm); + + + //System.setSecurityManager(new SecurityManager()); + + final Thing thing = new Thing(); + + + Permissions perms = new Permissions(); + //perms.add(new AllPermission()); + //perms.add(new FilePermission("hack.txt", "read,write")); + // perms.add(new NetPermission("*")); + //perms.add(new RuntimePermission("accessDeclaredMembers")); + // Cast to Certificate[] required because of ambiguity: + ProtectionDomain domain = new ProtectionDomain(new CodeSource(null, (Certificate[]) null), perms); + AccessControlContext ac = new AccessControlContext( + new ProtectionDomain[]{domain}); + + // This doesn't work: security permissions are lost in the next thing. + // bloody odd if you ask me + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public ScriptEngine run() { + ScriptEngineManager factory = new ScriptEngineManager(); + engine = factory.getEngineByName("JavaScript"); + return null; + } + }); + + + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + ScriptEngineManager factory = new ScriptEngineManager(); + engine = factory.getEngineByName("JavaScript"); + + ScriptContext ctx = new SimpleScriptContext(); + ctx.getBindings(ScriptContext.ENGINE_SCOPE); + + System.out.println("threaded = " + engine.getFactory().getParameter("THREADING")); + + engine.put("trigger", thing); + + Runnable r = new Runnable() { + @Override + public void run() { + try { + String s = "var out = new java.io.FileOutputStream('hack-thread.txt');" + + " out.write(65);" + + "out.close();" + + " println('write hack.txt');"; + engine.eval(s); + } catch (ScriptException ex) { + ex.printStackTrace(System.out); + } catch (AccessControlException x) { + x.printStackTrace(System.out); + } + } + }; + Thread t = new Thread(r); + t.start(); + + try { + engine.eval("println(\"hello world\");"); + engine.eval("trigger.chat('this is a chat message from sandbox?');"); + } catch (ScriptException ex) { + ex.printStackTrace(System.out); + } catch (AccessControlException x) { + x.printStackTrace(System.out); + } + try { + String s = "if (!trigger.isPet() &&" + + "trigger.hasCondition(\"walk\")) {" + + " trigger.removeCondition(\"walk\");" + + "trigger.moveTo(177,195);" + + "}"; + + engine.eval(s); + } catch (ScriptException ex) { + ex.printStackTrace(System.out); + } catch (AccessControlException x) { + x.printStackTrace(System.out); + } + try { + String s = "var out = java.lang.System.out;" + + " out.println('system out');"; + engine.eval(s); + } catch (ScriptException ex) { + ex.printStackTrace(System.out); + } catch (AccessControlException x) { + x.printStackTrace(System.out); + } + try { + String s = "var out = new java.io.FileOutputStream('hack.txt');" + + " out.write(65);" + + "out.close();" + + " println('write hack.txt');"; + engine.eval(s); + } catch (ScriptException ex) { + ex.printStackTrace(System.out); + } catch (AccessControlException x) { + x.printStackTrace(System.out); + } + + try { + String s = "var out = new java.net.Socket('localhost', 7475);;" + + " println(out);"; + engine.eval(s); + } catch (ScriptException ex) { + ex.printStackTrace(System.out); + } catch (AccessControlException x) { + x.printStackTrace(System.out); + } + try { + String s = "var out = new java.lang.Thread();" + + " println(out);"; + engine.eval(s); + } catch (ScriptException ex) { + ex.printStackTrace(System.out); + } catch (AccessControlException x) { + x.printStackTrace(System.out); + } + return null; + } + }, ac); + + try (FileOutputStream fos = new FileOutputStream("not-hack-b")) { + fos.write(1); + System.out.println("wrote not-hack-b"); + } catch (IOException x) { + x.printStackTrace(); + } catch (AccessControlException x) { + x.printStackTrace(System.out); + } + + } +} diff --git a/DuskServer/src/duskz/server/Battle.java b/DuskServer/src/duskz/server/Battle.java new file mode 100644 index 0000000..87418ed --- /dev/null +++ b/DuskServer/src/duskz/server/Battle.java @@ -0,0 +1,723 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - Pretty major cleanup and parameterisation of code. + */ +package duskz.server; + +import duskz.protocol.MessageType; +import duskz.server.entity.Mob; +import duskz.server.entity.LivingThing; +import duskz.server.entity.Item; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; + +/** + * Battle represents a fight between two sides comprised of LivingThings. + * + * TODO: Remove the general chat stuff and use specific message types where appropriate + * + * @author Tom Weingarten + */ +public class Battle { + + private ArrayList vctSide1 = new ArrayList<>(), + vctSide2 = new ArrayList<>(); + DuskEngine engGame; + boolean blnRunning = true, + blnPlayerSide1 = false, + blnPlayerSide2 = false; + private HashMap> commands = new HashMap<>(); + + Battle(LivingThing inpla1, LivingThing inpla2, DuskEngine inengGame) { + LivingThing thnFront1, + thnFront2; + try { + engGame = inengGame; + thnFront2 = inpla2; + while (thnFront2 != null) { + blnPlayerSide2 = addToBattle(2, vctSide2, vctSide1, thnFront2, blnPlayerSide2, blnPlayerSide1); + //addToBattle(thnFront2, 2); + thnFront2 = thnFront2.getFollowing(); + } + thnFront2 = inpla2.getMaster(); + while (thnFront2 != null) { + blnPlayerSide2 = addToBattle(2, vctSide2, vctSide1, thnFront2, blnPlayerSide2, blnPlayerSide1); + //addToBattle(thnFront2, 2); + thnFront2 = thnFront2.getMaster(); + } + thnFront2 = inpla2; + thnFront1 = inpla1; + while (thnFront1 != null) { + //addToBattle(thnFront1, 1); + blnPlayerSide1 = addToBattle(1, vctSide1, vctSide2, thnFront1, blnPlayerSide1, blnPlayerSide2); + thnFront1 = thnFront1.getFollowing(); + } + thnFront1 = inpla1.getMaster(); + while (thnFront1 != null) { + blnPlayerSide1 = addToBattle(1, vctSide1, vctSide2, thnFront1, blnPlayerSide1, blnPlayerSide2); + //addToBattle(thnFront1, 1); + thnFront1 = thnFront1.getMaster(); + } + thnFront1 = inpla1; + engGame.chatMessage("-" + inpla1.name + " has attacked " + inpla2.name, inpla1.x, inpla1.y, "default"); + if (inpla1.popup) { + inpla1.send(MessageType.StartBattle, inpla2.name + "\n"); + } + if (inpla2.popup) { + inpla2.send(MessageType.StartBattle, inpla1.name + "\n"); + } + } catch (Exception e) { + blnRunning = false; + engGame.log.printError("Battle()", e); + } + } + + public static LivingThing getEnemy(LivingThing lt) { + if (lt.battleSide == 1) { + return (LivingThing) lt.battle.vctSide2.get(0); + } else if (lt.battleSide == 2) { + return (LivingThing) lt.battle.vctSide1.get(0); + } else { + return null; + } + } + + public void addToBattle(LivingThing lt, int side) { + if (side == 1) { + blnPlayerSide1 = addToBattle(1, vctSide1, vctSide2, lt, blnPlayerSide1, blnPlayerSide2); + } else { + blnPlayerSide2 = addToBattle(2, vctSide2, vctSide1, lt, blnPlayerSide2, blnPlayerSide1); + } + } + + boolean addToBattle(int sideid, ArrayList side, ArrayList opponents, LivingThing thnAdded, boolean playerSide, boolean playerOpponent) { + if (thnAdded.isPlayer()) { + if (playerOpponent) { + if (thnAdded.clan.equals("none")) { + thnAdded.chatMessage("Players who are not in clans cannot fight other players."); + thnAdded.removeFromGroup(); + return playerSide; + } + LivingThing thnStore = side.get(0); + if (thnStore.clan.equals("none")) { + thnAdded.chatMessage("Players who are not in clans cannot fight other players."); + thnAdded.removeFromGroup(); + return playerSide; + } + } + playerSide = true; + if (engGame.blnMusic) { + thnAdded.playMusic(1); + } + } + chatMessage("\t" + thnAdded.name + " has joined the battle."); + side.add(thnAdded); + thnAdded.enterBattle(this, sideid); + + for (LivingThing lt : side) { + if (lt.highlight) { + lt.updateFlag(thnAdded.ID, 1); + } + if (thnAdded.highlight) { + thnAdded.updateFlag(lt.ID, 1); + } + } + for (LivingThing lt : opponents) { + if (lt.highlight) { + lt.updateFlag(thnAdded.ID, 2); + } + if (thnAdded.highlight) { + thnAdded.updateFlag(lt.ID, 2); + } + } + return playerSide; + } + + String attack(LivingThing attackor, LivingThing attackee, int range, String s) { + // The farther away the target, the harder they are to hit. + // The more skilled the attecker is in ranged combat, the less range affects ability to hit. + // The farther away the target, the easier it is for them to dodge. + // The more skilled the attecker is in ranged combat, the harder it is for the target to dodge. + int r2 = 0; + if (range > 1) { +// r2= thnAttacking.getSkill("ranged combat") - (int)((range * range - 1) * (double)(100/engGame.viewrange)); + r2 = attackor.getSkill("ranged combat"); + } else { + r2 = attackor.getSkill("close combat"); + } + if (r2 < 0) { + s += attackor.name + " missed."; + hitMessage(attackee.getID(), 0, attackee.hp, attackee.maxhp, attackor.getID(), "Missed!"); + } else if (dodge(attackor, attackee, r2)) { + s += attackee.name + " dodged " + attackor.name + "'s attack"; + hitMessage(attackee.getID(), 0, attackee.hp, attackee.maxhp, attackor.getID(), "Dodged!"); + } else { + if (engGame.battlesound != -1) { + engGame.playSound(engGame.battlesound, attackee.x, attackee.y); + } + int i = damRoll(attackor, attackee, range); + if (i < 0) { + i = 0; + } + s += attackor.name + " did " + i + " to " + attackee.name; + attackee.hp -= i; + attackor.damageDone += i; + attackor.weaponDam(i); + attackee.armorDam(i); + + hitMessage(attackee.getID(), i, attackee.hp, attackee.maxhp, attackor.getID(), "Hit!"); + } + return s; + } + + /** + * Attack everything in side1 against side2 + * + * @param list1 + * @param list2 + * @return true if the battle continues + */ + boolean attackSide(ArrayList list1, ArrayList list2) { + int range; + LivingThing target; + String msg; + + checkCommands(list1, list2); + if (list1.isEmpty()) { + endBattle(); + return false; + } + + for (LivingThing attackor : list1) { + msg = ""; + if (attackor.isMob()) { + Mob mobStore = (Mob) attackor; + mobStore.onBattle(engGame); + } + + range = Integer.MAX_VALUE; + target = null; + attackor.isMoveable = true; + for (LivingThing t : list2) { + int distance = attackor.distance(t); + if (distance < range) { + range = distance; + target = t; + } + /* + They are allowed to move until they are at a range of 1 or less with any target + */ + t.isMoveable = true; + if (attackor.adjacent(t)) { + attackor.isMoveable = false; + t.isMoveable = false; + } + } + + /* + End the battle if the closest target is off of the screen. + */ + if (range > engGame.viewrange) { + endBattle(); + return false; + } + if (range > attackor.getRangeWithBonus()) { + msg = attackor.name + " is out of range.\n"; + if (attackor.isMob() && !attackor.hasPendingMoves()) // if mob, try to go to the enemy to fight them + { + attackor.goTo(target.x, target.y, false); + } + } else { + if (attackor.isMob()) + attackor.clearMoveQueue(); + msg = attack(attackor, target, range, msg); + if (((Math.random() * 100) + 1) < attackor.getSkill("double attack")) { + msg += ","; + msg = attack(attackor, target, range, msg); + } + if (((Math.random() * 100) + 1) < attackor.getSkill("triple attack")) { + msg += ","; + msg = attack(attackor, target, range, msg); + } + if (((Math.random() * 100) + 1) < attackor.getSkill("quadruple attack")) { + msg += ","; + msg = attack(attackor, target, range, msg); + } + msg += "."; + } + chatMessage("\t" + msg); + } + return true; + } + + int damRoll(LivingThing attacker, LivingThing thnAttacked, int range) { + int attackerTotal = attacker.stre + attacker.strebon; + return (int) (((double) (attackerTotal) / 2) * (Math.random() + 0.5) * (attacker.getDamModWithBonus() / 100) - (thnAttacked.getArmorModWithBonus())); + } + + boolean dodge(LivingThing attacker, LivingThing thnAttacked, int attackModifier) { + int attackedTotal = thnAttacked.dext + thnAttacked.dextbon; + if (attackedTotal > 100) { + attackedTotal = 100; + } + int attackerTotal = attacker.dext + attacker.dextbon + attackModifier; + if (attackerTotal > 100) { + attackerTotal = 100 + attackModifier; + } + return ((Math.random() * 100) < ((thnAttacked.getSkill("Dodge") * .75) + (.25 * (attackedTotal - attackerTotal)))); + } + + void endBattle(ArrayList list) { + for (LivingThing lt : list) { + lt.clearFlags(); + lt.battle = null; + lt.battleSide = 0; + lt.isMoveable = true; + lt.updateInfo(); + lt.updateStats(); + lt.updateActions(); + lt.playMusic(0); + } + } + + void endBattle() { + blnRunning = false; + chatMessage("\tYou have won the battle."); + endBattle(vctSide1); + endBattle(vctSide2); + } + + void flee(LivingThing thnStore, ArrayList list, ArrayList opponents) { + thnStore.clearFlags(); + + updateFlagsSelf(vctSide1, 0); + updateFlagsSelf(vctSide2, 0); + + if (thnStore.getFollowing() != null && thnStore.getFollowing().isPet()) { + if (thnStore.getMaster() != null) { + thnStore.getMaster().setFollowing(thnStore.getFollowing().getFollowing()); + } + if (thnStore.getFollowing().getFollowing() != null) { + thnStore.getFollowing().getFollowing().setMaster(thnStore.getMaster()); + } + } else { + if (thnStore.getMaster() != null) { + thnStore.getMaster().setFollowing(thnStore.getFollowing()); + } + if (thnStore.getFollowing() != null) { + thnStore.getFollowing().setMaster(thnStore.getMaster()); + } + } + list.remove(thnStore); + if (thnStore.popup) { + thnStore.send("" + (char) 33 + "You have fled from battle\n"); + } else { + thnStore.chatMessage("You have fled from battle"); + } + splitMoney(thnStore, (int) (thnStore.cash * engGame.gpfleemod), opponents); + splitExp(thnStore, (int) (thnStore.exp * engGame.expfleemod), opponents); + thnStore.leaveBattle(); + thnStore.updateInfo(); + thnStore.updateStats(); + thnStore.updateActions(); + thnStore.playMusic(0); + if (thnStore.getFollowing() != null && thnStore.getFollowing().isPet()) { + list.remove(thnStore.getFollowing()); + updateFlags(vctSide1, thnStore.getFollowing().ID, 0); + updateFlags(vctSide2, thnStore.getFollowing().ID, 0); + thnStore.getFollowing().leaveBattle(); + } + if (vctSide2.size() == 0 || vctSide1.size() == 0) { + endBattle(); + return; + } + } + + void splitMoney(LivingThing thnStore, int money, ArrayList opponents) { + if (money == 0) { + return; + } + if (thnStore.popup) { + thnStore.send("" + (char) 33 + "You have lost " + money + " gp.\n"); + } else { + thnStore.chatMessage("You have lost " + money + " gp."); + } + thnStore.cash -= money; + int i, + i2 = 0; + LivingThing thnStore2; + try { + /* + for (i=0;i opponents) { + if (thnFront.popup) { + thnFront.send("" + (char) 33 + "You have lost " + exp + " exp.\n"); + } else { + thnFront.chatMessage("You have lost " + exp + " exp."); + } + thnFront.exp -= exp; + double tp, + sidepoints = 0; + //LivingThing thnFront; + //if (thnFront.bytSide == 1) { + // thnFront = vctSide1.get(0); + //} else { + // thnFront = vctSide2.get(0); + //} + int i, + i2 = 0; + LivingThing thnStore2; + + tp = thnFront.getTotalPoints(); + thnFront.damageDone = 0; + for (i = 0; i < opponents.size(); i++) { + thnStore2 = (LivingThing) opponents.get(i); +// added "|| thnStore2.isPet()" +// and changed "thnStore.getTotalPoints()" to "thnStore2.getTotalPoints()" +// if (thnStore2.isPlayer() || thnStore2.isPet()) +// { + sidepoints += thnStore2.getTotalPoints(); +// } + } + try { + for (i = 0; i < opponents.size(); i++) { + i2 = 0; + thnStore2 = (LivingThing) opponents.get(i); + if (!thnStore2.isMob()) { + if (thnStore2.damageDone > thnFront.maxhp) { + thnStore2.damageDone = thnFront.maxhp; + } + i2 = (int) (engGame.expgainmod + * (((tp / sidepoints) + + (2 * (thnStore2.damageDone / (double) thnFront.maxhp) * (tp / (double) thnStore2.getTotalPoints()))) / 3)); + thnStore2.chatMessage("You get " + i2 + " exp."); + thnStore2.exp += i2; + } + thnStore2.damageDone = 0; + } + } catch (Exception e) { + engGame.log.printError("splitExp()", e); + } + } + + void chatMessage(ArrayList side1, ArrayList side2, String strStore) { + LivingThing thnStore = null; + String strStore2 = null; + if (!side2.isEmpty()) { + thnStore = (LivingThing) side2.get(0); + strStore2 = thnStore.name + " has " + thnStore.getCharacterPoints() + "cp and " + thnStore.hp + "/" + thnStore.maxhp + "hp."; + } + for (int i = 0; i < side1.size(); i++) { + thnStore = (LivingThing) side1.get(i); + if (thnStore.isPlayer()) { + if (thnStore.popup) { + if (strStore2 != null) { + thnStore.send(MessageType.UpdateBattle, strStore2 + "\n"); + thnStore.send(MessageType.LogBattle, strStore + "\n"); + } + } else { + thnStore.chatMessage(strStore); + } + } else if (thnStore.isPet()) { + if (thnStore.getMaster().battle != thnStore.battle) { + if (thnStore.getMaster().popup) { + if (strStore2 != null) { + thnStore.getMaster().send(MessageType.UpdateBattle, "From " + thnStore.name + ": " + strStore2 + "\n"); + thnStore.getMaster().send(MessageType.LogBattle, "From " + thnStore.name + ": " + strStore + "\n"); + } + } else { + thnStore.chatMessage(strStore); + } + } + } + } + } + + void chatMessage(String strStore) { + chatMessage(vctSide1, vctSide2, strStore); + chatMessage(vctSide2, vctSide1, strStore); + } + + void battleMessage(ArrayList side1, MessageType type, String msg) { + for (LivingThing thnStore : side1) { + if (thnStore.isPlayer()) { + thnStore.send(type, msg); + } else if (thnStore.isPet()) { + if (thnStore.getMaster().battle != thnStore.battle) { + thnStore.send(type, msg); + } + } + } + } + + /** + * New battle interface with more detailed messages + * + * @param type + * @param msg + */ + void battleMessage(MessageType type, String msg) { + battleMessage(vctSide1, type, msg); + battleMessage(vctSide2, type, msg); + } + + void hitMessage(long targetid, int delta, int targethp, int targettotalhp, long fromid, String what) { + battleMessage(MessageType.HitEntity, + String.format("%d\n%d\n%d\n%d\n%d\n%s\n.\n", targetid, delta, targethp, targettotalhp, fromid, what)); + } + + /** + * Add a command to a current battle + * + * @param lt + * @param cmd + */ + public void addCommand(LivingThing lt, String cmd) { + synchronized (commands) { + LinkedList clist = commands.get(lt.ID); + if (clist == null) { + clist = new LinkedList<>(); + commands.put(lt.ID, clist); + } + clist.add(cmd); + } + } + + /** + * Add an expidited command to the current battle. i.e. flee + * + * @param lt + * @param cmd + */ + public void addFirstCommand(LivingThing lt, String cmd) { + synchronized (commands) { + LinkedList clist = commands.get(lt.ID); + if (clist == null) { + clist = new LinkedList<>(); + commands.put(lt.ID, clist); + } + clist.addFirst(cmd); + } + } + + void checkCommands(ArrayList list, ArrayList opponents) { + for (LivingThing lt : list) { + String cmd = null; + synchronized (commands) { + LinkedList clist = commands.get(lt.ID); + if (clist != null) { + cmd = clist.removeFirst(); + } + } + if (cmd != null) { + if (cmd.equalsIgnoreCase("flee")) { + flee(lt, list, opponents); + } else if (cmd.startsWith("cast ")) { + cmd = cmd.substring(5, cmd.length()); + lt.castSpell(cmd); + } else if (cmd.startsWith("use ")) { + cmd = cmd.substring(4, cmd.length()); + lt.useItem(cmd, -1); + } else if (cmd.startsWith("eat ")) { + cmd = cmd.substring(4, cmd.length()); + lt.useItem(cmd, Item.FOOD); + } else if (cmd.startsWith("drink ")) { + cmd = cmd.substring(6, cmd.length()); + lt.useItem(cmd, Item.DRINK); + } + } + } + } + + void update(ArrayList list) { + for (LivingThing lt : list) { + if (lt.isPlayer()) { + lt.updateInfo(); + lt.updateStats(); + } + } + } + + void update() { + update(vctSide1); + update(vctSide2); + } + + void updateFlags(ArrayList list, long ID, int flag) { + for (LivingThing lt : list) { + lt.updateFlag(ID, flag); + } + } + + void updateFlagsSelf(ArrayList list, int flag) { + for (LivingThing lt : list) { + lt.updateFlag(lt.ID, flag); + } + } + + void checkDeath(ArrayList vctSide1, ArrayList vctSide2) { + // FIXME: verify this works. this protects the second call + if (vctSide1.isEmpty() || vctSide2.isEmpty()) + return; + + LivingThing thnFront1 = vctSide1.get(0); + LivingThing thnFront2 = vctSide2.get(0); + + if (thnFront2.hp < 1 || !thnFront2.isWorking) { + vctSide2.remove(0); + + System.out.println(thnFront2 + " died\nRemaining in fight:"); + for (LivingThing lt : vctSide2) { + System.out.println(" " + lt); + } + + thnFront2.clearFlags(); + updateFlags(vctSide1, thnFront2.ID, 0); + updateFlags(vctSide2, thnFront2.ID, 0); + if (thnFront2.isPlayer()) { + thnFront2.removeFromGroup(); + chatMessage("\t" + thnFront2.name + " is killed."); + if (thnFront2.popup) { + thnFront2.send("" + (char) 33 + "\tYou have died.\n"); + } else { + thnFront2.chatMessage("\tYou have died."); + } + splitMoney(thnFront2, (int) (thnFront2.cash * engGame.gplosemod), vctSide1); + splitExp(thnFront2, (int) (thnFront2.exp * engGame.explosemod), vctSide1); + thnFront2.leaveBattle(); + thnFront2.updateInfo(); + thnFront2.updateStats(); + thnFront2.updateActions(); + thnFront2.playMusic(0); + engGame.chatMessage(thnFront2.name + " has been killed by " + thnFront1.name, "default"); + if (engGame.scrOnDeath != null && thnFront2.isWorking) { + engGame.scrOnDeath.varVariables.clearVariables(); + engGame.scrOnDeath.varVariables.addVariable("trigger", thnFront2); + engGame.scrOnDeath.varVariables.addVariable("killer", thnFront1); + engGame.scrOnDeath.runScript(); + } + if (thnFront2.getFollowing() != null && thnFront2.getFollowing().isPet()) { + vctSide2.remove(thnFront2.getFollowing()); + updateFlags(vctSide1, thnFront2.getFollowing().ID, 0); + updateFlags(vctSide2, thnFront2.getFollowing().ID, 0); + thnFront2.getFollowing().leaveBattle(); + thnFront2.getFollowing().damageDone = 0; + thnFront2.getFollowing().changeLocBypass(thnFront2.x, thnFront2.y); + } + } else if (thnFront2.isMob()) { + Mob mob = (Mob) thnFront2; + chatMessage("\t" + thnFront2.name + " is killed."); + splitMoney(thnFront2, (int) (thnFront2.cash), vctSide1); + splitExp(thnFront2, 0, vctSide1); + /** + * FIXME: Mobs are not fully removed from the game when they die. + * This seems very questionable, shouldn't they just be changed + * to "dead"? + */ + mob.leaveBattle(); + // if (mob.blnOneUse) { + // engGame.removeDuskObject(mob); + // } else { + // //engGame.removeDuskObject(mob); + // // Marks mob as "dead" + // mob.leaveBattle(); + // } + for (LivingThing lt : vctSide1) { + if (!lt.isPet()) { + try { + mob.fctFaction.killedBy(mob, lt); + } catch (Exception e) { + engGame.log.printError("Battle.run():While updating faction for mob " + mob.name, e); + } + } + } + if (thnFront1.isPlayer()) { + Item itmStore; + for (GiveItem gitStore : mob.vctGiveItems) { + if (Math.random() < gitStore.dblProbability) { + itmStore = engGame.getItem(gitStore.strItemName); + if (itmStore != null) { + // FIXME: giveItem() on livingthing? + thnFront1.chatMessage("You got a " + gitStore.strItemName + "."); + thnFront1.itemList.addElement(itmStore); + itmStore.onGetItem(engGame, thnFront1); + thnFront1.updateItems(); + } + } + } + } + } else if (thnFront2.isPet()) { + chatMessage("\t" + thnFront2.name + " is wounded."); + thnFront2.chatMessage("\tYou have been wounded."); + splitMoney(thnFront2, (int) (thnFront2.cash * engGame.gplosemod), vctSide1); + splitExp(thnFront2, (int) (thnFront2.exp * engGame.explosemod), vctSide1); + thnFront2.leaveBattle(); + } + //if (!vctSide2.isEmpty()) { + // thnFront2 = vctSide2.get(0); + //} + } + } + + public void run() { + if (!attackSide(vctSide1, vctSide2)) { + return; + } + if (!attackSide(vctSide2, vctSide1)) { + return; + } + + checkDeath(vctSide1, vctSide2); + checkDeath(vctSide2, vctSide1); + + if (vctSide2.isEmpty() || vctSide1.isEmpty()) { + endBattle(); + return; + } + update(); + } +} diff --git a/DuskServer/src/duskz/server/Commands.java b/DuskServer/src/duskz/server/Commands.java new file mode 100644 index 0000000..01a790c --- /dev/null +++ b/DuskServer/src/duskz/server/Commands.java @@ -0,0 +1,2581 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - modernised java + */ +package duskz.server; + +import duskz.server.entity.Mob; +import duskz.server.entity.Merchant; +import duskz.server.entity.Sign; +import duskz.server.entity.Item; +import duskz.server.entity.Prop; +import duskz.server.entity.DuskObject; +import duskz.server.entity.Equipment; +import duskz.server.entity.PlayerMerchant; +import duskz.server.entity.LivingThing; +import java.io.*; +import java.util.LinkedList; +import java.util.StringTokenizer; + +public class Commands { + + public static String parseCommand(LivingThing lt, DuskEngine game, String cmdline) throws Exception { + if (cmdline == null) { + return null; + } + if (lt == null) { + return null; + } + if (game == null) { + return null; + } + + String cmd = null; + String args = null; + + int intIndex = cmdline.indexOf(" "); + if (intIndex == -1) { + cmd = cmdline.toLowerCase(); + } else { + cmd = cmdline.substring(0, intIndex).toLowerCase(); + args = cmdline.substring(intIndex + 1).trim(); + } + + if (cmd.length() < 1) { + return "huh?"; + } + + lt.isAlwaysCommands = true; + boolean blnFoundScriptedCommand = false; + /* + ** Don't try to find a scripted command if they are doing a tell + */ + if (cmd.substring(0, 1) != "/") { + try { + Script script = new Script("commands/" + cmd, game, false); + script.varVariables.addVariable("trigger", lt); + if (args != null) { + script.runScript(args); + } else { + script.runScript(); + } + script.close(); + blnFoundScriptedCommand = true; + } catch (Exception e) { + blnFoundScriptedCommand = false; + } + if (!lt.isAlwaysCommands) { + return null; + } + } + if (lt.privs > 2) { + if (cmdline.startsWith(">")) { + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + cmdline = cmdline.substring(1); + if (cmdline.equalsIgnoreCase("s")) { + if (game.blnSavingGame) { + return "Game already being saved, please wait."; + } + game.saveMap(); + return "Game settings saved"; + } + + String strMapLog = "shortmap_redraw"; + try (PrintStream psMap = new PrintStream(new FileOutputStream(strMapLog, true), true)) { + game.changeMap(lt.x, lt.y, Short.parseShort(cmdline)); + psMap.println("changetile " + lt.x + " " + lt.y + " " + Short.parseShort(cmdline)); + return null; + } catch (Exception e) { + game.log.printError("parseCommand():While " + lt.name + " tried to >" + cmdline, e); + return null; + } + } + if (cmdline.startsWith("<")) { + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + cmdline = cmdline.substring(1); + if (cmdline.equals("s")) { + if (game.blnSavingGame) { + return "Game already being saved, please wait."; + } + game.saveMap(); + return "Game settings saved"; + } else if (cmdline.equalsIgnoreCase("merchant")) { + if (game.overMerchant(lt.x, lt.y) != null) { + return "There's already a merchant there."; + } + if (game.overPlayerMerchant(lt.x, lt.y) != null) { + return "There's already a merchant there."; + } + Merchant mrcStore = new Merchant(game); + mrcStore.x = lt.x; + mrcStore.y = lt.y; + //game.vctMerchants.add(mrcStore); + //game.blnMerchantListChanged = true; + game.addDuskObject(mrcStore); + return null; + } else if (cmdline.toLowerCase().startsWith("prop ")) { + if (cmdline.length() == 5) { + return "Add what prop?"; + } + cmdline = cmdline.substring(5); + Prop prpStore = game.getProp(cmdline); + if (prpStore != null) { + prpStore.x = lt.x; + prpStore.y = lt.y; + //game.vctProps.addElement(prpStore); + //game.blnPropListChanged = true; + game.addDuskObject(prpStore); + } + return null; + } else if (cmdline.startsWith("sign ")) { + if (cmdline.length() == 5) { + return "What should the sign say?"; + } + Sign sgnStore = new Sign(game, "sign", cmdline.substring(5), lt.x, lt.y, game.getID()); + //game.vctSigns.add(sgnStore); + //game.blnSignListChanged = true; + game.addDuskObject(sgnStore); + return null; + } + Item itmStore = game.getItem(cmdline); + if (itmStore != null) { + itmStore.x = lt.x; + itmStore.y = lt.y; + //game.vctItems.add(itmStore); + game.addDuskObject(itmStore); + return null; + } + try { + Mob mob = new Mob(cmdline, lt.x, lt.y, game); + // TODO: this previously didn't call addDuskObject - bug or intentional? + //game.vctMobs.addElement(mob); + //game.blnMobListChanged = true; + game.addDuskObject(mob); + + mob.changeLocBypass(lt.x, lt.y); + } catch (Exception e) { + game.log.printError("parseCommand():While creating mob \"" + cmdline + "\"", e); + } + return null; + } + } + + // Remap shortcuts + if (cmd.startsWith(";")) { + args = cmdline.substring(1).trim(); + cmd = "gossip"; + } + if (cmd.startsWith(":")) { + args = cmdline.substring(1).trim(); + cmd = "clan"; + } + if (cmd.startsWith("'")) { + args = cmdline.substring(1).trim(); + cmd = "say"; + } + if (cmd.startsWith(".")) { + args = cmdline.substring(1).trim(); + cmd = "emote"; + } + if (cmd.startsWith("/")) { + args = cmdline.substring(1).trim(); + cmd = "tell"; + } + + switch (cmd) { + case "north": + case "n": + if (!lt.moveAfterClear("n")) { + return "You can't move while you're following someone."; + } + return null; + case "south": + case "s": + if (!lt.moveAfterClear("s")) { + return "You can't move while you're following someone."; + } + return null; + case "west": + case "w": + if (!lt.moveAfterClear("w")) { + return "You can't move while you're following someone."; + } + return null; + case "east": + case "e": + if (!lt.moveAfterClear("e")) { + return "You can't move while you're following someone."; + } + return null; + case "goto": { + int destX; + int destY; + try { + destX = Integer.parseInt(args.substring(0, args.lastIndexOf(' '))); + destY = Integer.parseInt(args.substring(args.lastIndexOf(' ') + 1)); + } catch (Exception e) { + game.log.printError("parseCommand():" + lt.name + " tried to " + cmdline, e); + return null; + } + return lt.goTo(destX, destY, true); + } + + case "addmember": { + if (lt.privs != 1) + return "Huh?"; + if (args == null) { + return "Add who?"; + } + LivingThing thnStore = game.getPlayer(args); + if (thnStore == null) { + return "They're not in this world"; + } + if (lt.battle != null) { + return "Not while you're fighting!"; + } + if (thnStore.battle != null) { + thnStore.chatMessage(lt.name + " has invited you to join their clan, but you are in the middle of a battle"); + return "They're in the middle of a battle. They have been notified that you tried to clan them."; + } + lt.chatMessage("You have invited " + thnStore.name + " to join the clan " + lt.clan + "."); + + if (true) + // FIXME: protocol implementation + throw new RuntimeException("cannot ask questions yet"); + // FIXME: this looks dodgy + // FIXME: move to livingthing + + thnStore.halt(); + thnStore.stillThere(); // This puts something in the buffer + thnStore.stillThere(); // Have to do this twice to ensure that thnStore is out of + // its read loop + lt.connectionThread.sleep(500); // wait for the "notdead" response to get back from client. + try { + // Empty out the BufferedReader for the answer + // while (thnStore.instream.ready()) { + // thnStore.instream.read(); + // } + } catch (Exception e) { + game.log.printError("parseCommand():While " + lt.name + " was trying to addmember " + thnStore.name, e); + } + thnStore.chatMessage(lt.name + " has invited you to join the clan " + lt.clan + ". If you accept, type yes."); + try { + if (thnStore.instream.readLine().equalsIgnoreCase("yes")) { + thnStore.clan = lt.clan; + if (thnStore.privs == 1) { + thnStore.privs = 0; + } + thnStore.chatMessage("You have been added to the clan, " + lt.clan + ""); + thnStore.proceed(); + game.removeDuskObject(thnStore); + game.addDuskObject(thnStore); + return thnStore.name + " has accepted your invitation."; + } + } catch (Exception e) { + game.log.printError("parseCommand():While reading the answer to " + lt.name + "'s attempt to addmember " + thnStore.name, e); + } + thnStore.proceed(); + return thnStore.name + " has declined your invitation."; + } + case "kick": { + if (lt.privs != 1) + return "Huh?"; + if (args == null) { + return "Kick who?"; + } + LivingThing thnStore = game.getPlayer(args); + if (thnStore == null) { + return "They're not in this world."; + } + if (!thnStore.clan.equalsIgnoreCase(lt.clan)) { + return "They're not in your clan."; + } + thnStore.chatMessage("You have been kicked out of " + lt.clan + "."); + thnStore.clan = "none"; + game.removeDuskObject(thnStore); + game.addDuskObject(thnStore); + return thnStore.name + " has been kicked out of your clan."; + } +// if (lt.privs > 2) { +// if (lt.privs > 4) { + case "makegod": { + if (lt.privs <= 4) + return "Huh?"; + + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + if (args == null) { + return "Make who a god?"; + } + int iSPloc = args.indexOf(" "); + if (iSPloc < 0) { + return "Make them what level of a god?"; + } + String sName = args.substring(0, iSPloc).trim(); + int level = Integer.parseInt(args.substring(iSPloc).trim()); + + LivingThing thnStore = game.getPlayer(sName); + if (thnStore == null) { + return "They're not in this world."; + } + int oldLevel = thnStore.privs; + thnStore.privs = level; + thnStore.isSaveNeeded = true; + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":Changed " + thnStore.name + "'s priveledges from " + oldLevel + " to " + level + "."); + return thnStore.name + "'s priveledges have been set to " + level + "."; + } + case "reloadprefs": { + if (lt.privs <= 4) + return "Huh?"; + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + game.loadPrefs(); + return "Preferences reloaded"; + } + case "resizemap": + if (lt.privs <= 4) + return "Huh?"; + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + game.resizeMap(lt.x + 1, lt.y + 1); + return "Map resized"; + case "shutdown": + if (lt.privs <= 4) + return "Huh?"; + game.log.printMessage(Log.ALWAYS, lt.name + " has shut down the server."); + game.chatMessage("The server is going down.", "default"); + game.blnShuttingDown = true; + for (LivingThing thnStore : game.playersByName.values()) { + try { + if (thnStore != lt) { + thnStore.close(); + } + } catch (Exception e) { + if (thnStore != null) { + game.log.printError("parseCommand():While trying to close " + thnStore.name + " for " + lt.name + "'s shutdown request", e); + } else { + game.log.printError("parseCommand():While trying to close a null player for " + lt.name + "'s shutdown request", e); + } + } + } + lt.isSaveNeeded = true; + lt.savePlayer(); + System.gc(); + System.exit(0); + return null; + case "stop": + if (lt.privs <= 4) + return "Huh?"; + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + game.blnShuttingDown = true; + return "Stopped accepting incoming socket connections."; + case "start": + if (lt.privs <= 4) + return "Huh?"; + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + game.blnShuttingDown = false; + return "Started accepting incoming connections"; + case "filteron": + if (lt.privs <= 4) + return "Huh?"; + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + game.blnIPF = true; + return "Started filtering duplicate IP addressess of socket connections."; + case "filteroff": + if (lt.privs <= 4) + return "Huh?"; + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + game.blnIPF = false; + return "Stopped filtering duplicate IP addressess of socket connections."; + case "floodlimit": + if (lt.privs <= 4) + return "Huh?"; + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + if (args == null) { + return "What value do you want the floodlimit to have?"; + } + try { + game.floodLimit = (long) Integer.parseInt(args); + return "Set floodlimit to " + args + " milliseconds."; + } catch (Exception e) { + game.log.printError("parseCommand():Invalid value \"" + args + "\" for floodlimit.", e); + return "Invalid value \"" + args + "\" for floodlimit."; + } + case "ipban": { + if (lt.privs <= 4) + return "Huh?"; + String strBlockedIP; + if (args == null) { + return "Whos IP address do you wish to ban?"; + } + LivingThing thnStore = game.getPlayer(args); + if (thnStore == null) { + return "They're not in this world."; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + String strIP = thnStore.getAddress(); + int i = strIP.indexOf("/"); + strIP = strIP.substring(i + 1, strIP.length()); + // FIXME: better i/o + try (RandomAccessFile rafBannedIP = new RandomAccessFile("conf/blockedIP", "rw")) { + strBlockedIP = rafBannedIP.readLine(); + while (strBlockedIP != null) { + if (strIP.indexOf(strBlockedIP) != -1) { + //rafBannedIP.close(); + return "Already blocked."; + } + strBlockedIP = rafBannedIP.readLine(); + } + rafBannedIP.seek(rafBannedIP.length()); + rafBannedIP.writeBytes(strIP + "\n"); + } catch (IOException ex) { + game.log.printError("parseCommand():When " + lt.name + " tried to ban " + thnStore + "'s IP address", ex); + } + return thnStore.name + "'s IP address has been blocked."; + } + case "loglevel": + if (lt.privs <= 4) + return "Huh?"; + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + if (args == null) { + return "Logging level is currently " + game.log.getLogLevel(); + } + try { + int level = Integer.parseInt(args); + game.log.setLogLevel(level); + return "Logging level is now " + game.log.getLogLevel(); + } catch (Exception e) { + game.log.printError("parseCommand():Invalid value \"" + args + "\" for loglevel.", e); + return "Invalid value \"" + args + "\" for loglevel."; + } + case "gc": + if (lt.privs <= 4) + return "Huh?"; + game.log.printMessage(Log.INFO, "Starting garbage collection."); + System.gc(); + game.log.printMessage(Log.INFO, "Finished garbage collection."); + return "Finished garbage collection."; + case "finalize": + if (lt.privs <= 4) + return "Huh?"; + game.log.printMessage(Log.INFO, "Starting finalization."); + System.runFinalization(); + game.log.printMessage(Log.INFO, "Finished finalization."); + return "Finished finalization."; + case "cleanup": + if (lt.privs <= 4) + return "Huh?"; + game.cleanup(); + return "Finished cleanup."; + case "save": + if (lt.privs <= 2) + return "Huh?"; + if (game.blnSavingGame) { + return "Game already being saved, please wait."; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + game.saveMap(); + return "Game settings saved"; + case "backup": + if (lt.privs <= 2) + return "Huh?"; + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + game.backupMap(); + return "Game settings backed up"; + case "list": { + if (lt.privs <= 2) + return "Huh?"; + if (args == null) { + return "What do you want to list?"; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + cmdline = cmdline.substring(5); + String filename = null; + String strTitle = null; + if (args.equals("items")) { + filename = "defItems"; + strTitle = "Items:\n"; + } else if (args.equals("conf")) { + filename = "conf"; + strTitle = "Conf files:\n"; + } else if (args.equals("mobs")) { + filename = "defMobs"; + strTitle = "Mobiles:\n"; + } else if (args.equals("commands")) { + filename = "commands"; + strTitle = "Custom commands:\n"; + } else if (args.equals("races")) { + filename = "defRaces"; + strTitle = "Races:\n"; + } else if (args.equals("pets")) { + filename = "defPets"; + strTitle = "Pets:\n"; + } else if (args.equals("factions")) { + filename = "factions"; + strTitle = "Factions:\n"; + } else if (args.equals("conditions")) { + filename = "defConditions"; + strTitle = "Conditions:\n"; + } else if (args.equals("help")) { + filename = "helpFiles"; + strTitle = "Help Files:\n"; + } else if (args.equals("scripts")) { + filename = "scripts"; + strTitle = "Scripts:\n"; + } else if (args.equals("spell groups")) { + filename = "defSpellGroups"; + strTitle = "Spell Groups:\n"; + } else if (args.equals("spells")) { + filename = "defSpells"; + strTitle = "Spells:\n"; + } else if (args.equals("props")) { + filename = "defProps"; + strTitle = "Props:\n"; + } else if (args.equals("move actions")) { + filename = "defMoveActions"; + strTitle = "Move Action Scripts:\n"; + } else if (args.equals("can move")) { + filename = "defCanMoveScripts"; + strTitle = "Can Move Scripts:\n"; + } else if (args.equals("can see")) { + filename = "defCanSeeScripts"; + strTitle = "Can See Scripts:\n"; + } else if (args.equals("tile actions")) { + filename = "defTileActions"; + strTitle = "Tile Action Scripts:\n"; + } else if (args.equals("tile move")) { + filename = "defTileScripts"; + strTitle = "Can Move Tile Scripts:\n"; + } else if (args.equals("tile see")) { + filename = "defTileSeeScripts"; + strTitle = "Tile See Scripts:\n"; + } + if (filename != null) { + File filList = new File(filename); + String strResult[] = filList.list(); + StringBuilder strBuff = new StringBuilder(); + strBuff.append("").append((char) 20).append(strTitle).append("\n"); + for (int i = 0; i < strResult.length; i++) { + // Only output files that do not end in .dsko + if (strResult[i].indexOf(".dsko") == -1) { + strBuff.append(strResult[i]).append("\n"); + } + } + strBuff.append("--EOF--\n"); + lt.send(strBuff.toString()); + return null; + } + return "You can't list that."; + } + case "view": { + if (lt.privs <= 2) + return "Huh?"; + + if (args == null) { + return "What do you want to view?"; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + if (args.indexOf("..") != -1) { + return "You don't have permission to access that file."; + } + String filename = null; + boolean blnUser = false; + boolean blnPet = false; + if (args.startsWith("item ")) { + args = args.toLowerCase(); + filename = "defItems/" + args.substring(5); + } else if (args.startsWith("conf ")) { + filename = "conf/" + args.substring(5); + } else if (args.startsWith("mob ")) { + filename = "defMobs/" + args.substring(4); + } else if (args.startsWith("command ")) { + filename = "commands/" + args.substring(8); + } else if (args.startsWith("race ")) { + filename = "defRaces/" + args.substring(5); + } else if (args.startsWith("pet ")) { + filename = "defPets/" + args.substring(5); + } else if (args.startsWith("faction")) { + return "You cannot view faction files."; + } else if (args.startsWith("condition ")) { + filename = "defConditions/" + args.substring(10); + } else if (args.startsWith("help ")) { + filename = "helpFiles/" + args.substring(5); + } else if (args.startsWith("script ")) { + filename = "scripts/" + args.substring(7); + } else if (args.startsWith("spell group ")) { + filename = "defSpellGroups/" + args.substring(12); + } else if (args.startsWith("spell ")) { + filename = "defSpells/" + args.substring(6); + } else if (args.startsWith("prop ")) { + filename = "defProps/" + args.substring(5); + } else if (args.startsWith("move action ")) { + filename = "defMoveActions/" + args.substring(12); + } else if (args.startsWith("can move ")) { + filename = "defCanMoveScripts/" + args.substring(9); + } else if (args.startsWith("can see ")) { + filename = "defCanSeeScripts/" + args.substring(8); + } else if (args.startsWith("tile action ")) { + filename = "defTileActions/" + args.substring(12); + } else if (args.startsWith("tile move ")) { + filename = "defTileScripts/" + args.substring(10); + } else if (args.startsWith("tile see ")) { + filename = "defTileSeeScripts/" + args.substring(9); + } else if (args.startsWith("user ")) { + if (lt.privs < 5) { + return "You don't have enough privelages to edit a user's file."; + } + blnUser = true; + filename = "users/" + args.substring(5); + } else if (args.startsWith("pet ")) { + if (lt.privs < 5) { + return "You don't have enough privelages to edit a user's pet file."; + } + blnPet = true; + filename = "pets/" + args.substring(4); + } + File filView = new File(filename); + if (!filView.exists()) { + if (blnUser) { + return "There is no player named \"" + filView.getName() + "\"."; + } + if (blnPet) { + return "The player named \"" + filView.getName() + "\" does not have a pet."; + } + lt.send((char) 18 + args + "\n--EOF--\n"); + return null; + } + RandomAccessFile rafView = null; + StringBuffer strBuff = new StringBuffer(); + try { + rafView = new RandomAccessFile(filView, "rw"); + if (blnUser) { + rafView.readLine(); //Skip over users' password + } + String strStore2 = rafView.readLine(); + strBuff.append((char) 18 + args + "\n"); + while (strStore2 != null) { + strBuff.append(strStore2 + "\n"); + strStore2 = rafView.readLine(); + } + strBuff.append("--EOF--\n"); + lt.send(strBuff.toString()); + } catch (Exception e) { + game.log.printError("parseCommand():Reading file for " + filView.getName(), e); + } + try { + rafView.close(); + } catch (Exception e) { + game.log.printError("parseCommand():Closing file after " + filView.getName(), e); + } + return null; + } + case "submit": { + if (lt.privs <= 2) + return "Huh?"; + if (args == null) { + return "What do you want to submit?"; + } + if ((lt.privs < 4) && (!args.startsWith("mob "))) { + return "You are not allowed to submit files."; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + if (args.indexOf("..") != -1) { + return "You don't have permission to access that file."; + } + boolean compile = false; + boolean blnUser = false; + boolean blnPet = false; + String strFileName = null; + if (args.startsWith("item ") && (lt.privs > 3)) { + args = args.toLowerCase(); + strFileName = "defItems/" + args.substring(5); + } else if (args.startsWith("conf ") && (lt.privs > 3)) { + strFileName = "conf/" + args.substring(5); + } else if (args.startsWith("mob ")) { + strFileName = "defMobs/" + args.substring(4); + } else if (args.startsWith("command ") && (lt.privs > 3)) { + strFileName = "commands/" + args.substring(8); + compile = true; + } else if (args.startsWith("race ") && (lt.privs > 3)) { + strFileName = "defRaces/" + args.substring(5); + } else if (args.startsWith("pet ") && (lt.privs > 3)) { + strFileName = "defPets/" + args.substring(4); + } else if (args.startsWith("faction") && (lt.privs > 3)) { + return "You cannot submit faction files."; + } else if (args.startsWith("condition ") && (lt.privs > 3)) { + strFileName = "defConditions/" + args.substring(10); + } else if (args.startsWith("help ") && (lt.privs > 3)) { + strFileName = "helpFiles/" + args.substring(5); + } else if (args.startsWith("script ") && (lt.privs > 3)) { + strFileName = "scripts/" + args.substring(7); + compile = true; + } else if (args.startsWith("spell group ") && (lt.privs > 3)) { + strFileName = "defSpellGroups/" + args.substring(12); + compile = true; + } else if (args.startsWith("spell ") && (lt.privs > 3)) { + strFileName = "defSpells/" + args.substring(6); + } else if (args.startsWith("prop ")) { + strFileName = "defProps/" + args.substring(5); + } else if (args.startsWith("move action ") && (lt.privs > 3)) { + strFileName = "defMoveActions/" + args.substring(12); + compile = true; + } else if (args.startsWith("can move ") && (lt.privs > 3)) { + strFileName = "defCanMoveScripts/" + args.substring(9); + compile = true; + } else if (args.startsWith("can see ") && (lt.privs > 3)) { + strFileName = "defCanSeeScripts/" + args.substring(8); + compile = true; + } else if (args.startsWith("tile action ") && (lt.privs > 3)) { + strFileName = "defTileActions/" + args.substring(12); + compile = true; + } else if (args.startsWith("tile move ") && (lt.privs > 3)) { + strFileName = "defTileScripts/" + args.substring(10); + compile = true; + } else if (args.startsWith("tile see ") && (lt.privs > 3)) { + strFileName = "defTileSeeScripts/" + args.substring(9); + compile = true; + } else if (args.startsWith("user ")) { + if (lt.privs < 5) { + return "You don't have enough privelages to submit a user's file."; + } + if (game.getPlayer(args.substring(5)) != null) { + return "You cannot submit a file for an active user."; + } + blnUser = true; + strFileName = "users/" + args.substring(5); + } else if (args.startsWith("pet ")) { + if (lt.privs < 5) { + return "You don't have enough privelages to submit a user's pet file."; + } + if (game.getPlayer(args.substring(4)) != null) { + return "You cannot submit a pet file for an active user."; + } + blnPet = true; + strFileName = "pets/" + args.substring(4); + } + if (strFileName == null) { + return "Cannot submit " + args; + } + File filView = null; + try { + filView = new File(strFileName); + } catch (Exception e) { + return "Cannot submit " + args + " (" + strFileName + ")"; + } + RandomAccessFile rafView = null; + try { + if (blnUser) { + /* + Read in the user's password before deleting the file + */ + rafView = new RandomAccessFile(filView, "r"); + cmdline = rafView.readLine(); + } + if (filView.exists()) { + filView.delete(); + } + rafView = new RandomAccessFile(filView, "rw"); + if (blnUser) { + /* + Write out the password for user files + */ + rafView.writeBytes(cmdline + "\n"); + } + cmdline = lt.instream.readLine(); + while (!cmdline.equals("--EOF--")) { + rafView.writeBytes(cmdline + "\n"); + cmdline = lt.instream.readLine(); + } + rafView.close(); + if (compile) { + Script scrStore = new Script(filView.getPath(), game, true); + scrStore.close(); + } + if (blnUser || blnPet) { + /* + Delete the .backup file for users and pets + */ + filView = new File(strFileName + ".backup"); + if (filView.exists()) { + filView.delete(); + } + } + } catch (Exception e) { + game.log.printError("parseCommand():While submitting file " + args + " (" + filView.getName() + ")", e); + try { + rafView.close(); + } catch (Exception e2) { + game.log.printError("parseCommand():While closing file " + args + " (" + filView.getName() + ") after failed submit", e); + } + return "Error while trying to submit " + args + " (" + filView.getName() + ")."; + } + return "Submit of " + args + " was successful."; + } + case "delete": { + if (lt.privs <= 2) + return "Huh?"; + if (args == null) { + return "What do you want to delete?"; + } + if (lt.privs < 4) { + return "You are not allowed to delete files."; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + if (args.indexOf("..") != -1) { + return "You don't have permission to access that file."; + } + String filename = null; + String strReturn = null; + if (args.startsWith("item ")) { + filename = "defItems/" + args.substring(5); + strReturn = "item " + args.substring(5); + } else if (args.startsWith("conf ")) { + filename = "conf/" + args.substring(5); + strReturn = "conf " + args.substring(5); + } else if (args.startsWith("mob ")) { + filename = "defMobs/" + args.substring(4); + strReturn = "mob " + args.substring(4); + } else if (args.startsWith("command ")) { + filename = "commands/" + args.substring(8); + strReturn = "command " + args.substring(8); + } else if (args.startsWith("race ")) { + filename = "defRaces/" + args.substring(5); + strReturn = "race " + args.substring(5); + } else if (args.startsWith("pet ")) { + filename = "defPets/" + args.substring(4); + strReturn = "pet " + args.substring(4); + } else if (args.startsWith("faction")) { + return "You cannot delete faction files."; + } else if (args.startsWith("condition ")) { + filename = "defConditions/" + args.substring(8); + strReturn = "condition " + args.substring(8); + } else if (args.startsWith("help ")) { + filename = "helpFiles/" + args.substring(5); + strReturn = "help " + args.substring(5); + } else if (args.startsWith("script ")) { + filename = "scripts/" + args.substring(7); + strReturn = "script " + args.substring(7); + } else if (args.startsWith("spell group ")) { + filename = "defSpellGroups/" + args.substring(12); + strReturn = "spell group " + args.substring(12); + } else if (args.startsWith("spell ")) { + filename = "defSpells/" + args.substring(6); + strReturn = "spell " + args.substring(6); + } else if (args.startsWith("prop ")) { + filename = "defProps/" + args.substring(5); + strReturn = "prop " + args.substring(5); + } else if (args.startsWith("move action ")) { + filename = "defMoveActions/" + args.substring(12); + strReturn = "move action " + args.substring(12); + } else if (args.startsWith("can move ")) { + filename = "defCanMoveScripts/" + args.substring(9); + strReturn = "can move " + args.substring(9); + } else if (args.startsWith("can see ")) { + filename = "defCanSeeScripts/" + args.substring(8); + strReturn = "can see " + args.substring(8); + } else if (args.startsWith("tile action ")) { + filename = "defTileActions/" + args.substring(12); + strReturn = "tile action " + args.substring(12); + } else if (args.startsWith("tile move ")) { + filename = "defTileScripts/" + args.substring(10); + strReturn = "tile move " + args.substring(10); + } else if (args.startsWith("tile see ")) { + filename = "defTileSeeScripts/" + args.substring(9); + strReturn = "tile see " + args.substring(9); + } + File filDelete = null; + if (filename != null) { + filDelete = new File(filename); + if (filDelete.exists()) { + filDelete.delete(); + filDelete = new File(filename + ".dsko"); + if (filDelete.exists()) { + filDelete.delete(); + strReturn += " and the associated .dsko file."; + } + } else { + return strReturn + " does not exist."; + } + return "Deleted " + strReturn; + } + return "You cannot delete that."; + } + case "clanleader": { + if (lt.privs <= 2) + return "Huh?"; + + if (args == null) { + return "Clanleader who?"; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + LivingThing thnStore = game.getPlayer(args.substring(0, args.indexOf(' '))); + if (thnStore == null) { + return "They're not in this world"; + } + if (args.length() < thnStore.name.length() + 2) { + return "What clan?"; + } + if (thnStore.privs > 1) { + return "You can't clanleader them."; + } + args = args.substring(thnStore.name.length() + 1); + thnStore.clan = args; + if (args.equals("none")) { + thnStore.privs = 0; + thnStore.chatMessage("You are now clanless."); + } else { + thnStore.privs = 1; + thnStore.chatMessage("You are now a member of the " + args + " clan."); + } + game.removeDuskObject(thnStore); + game.addDuskObject(thnStore); + return thnStore.name + " is now a leader of the " + args + " clan."; + } + case "boot": { + if (lt.privs <= 2) + return "Huh?"; + if (args == null) { + return "Boot who?"; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + LivingThing thnStore = game.getPlayer(args); + if (thnStore == null) { + return "They're not in this world."; + } + if (thnStore.privs >= lt.privs) { + thnStore.chatMessage(lt.name + " attempted to boot you."); + return "You do not have high enough privelages to boot them."; + } + thnStore.chatMessage("You have been booted."); + thnStore.close(); + return null; + } + case "hardkill": { + if (lt.privs <= 2) + return "Huh?"; + if (args == null) { + return "HardKill who?"; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + LivingThing thnStore = game.getPlayer(args); + if (thnStore == null) { + return "They're not in this world."; + } + if (thnStore.privs >= lt.privs) { + thnStore.chatMessage(lt.name + " attempted to HardKill you."); + return "You do not have high enough privelages to HardKill them."; + } + thnStore.closeNoMsgPlayer(); + return null; + } + case "nochannel": { + if (lt.privs <= 2) + return "Huh?"; + if (args == null) { + return "nochannel who for how long?"; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + LivingThing thnStore; + int duration; + try { + thnStore = game.getPlayer(args.substring(0, args.indexOf(" "))); + duration = Integer.parseInt(args.substring(args.indexOf(" ") + 1)); + } catch (Exception e) { + game.log.printError("parseCommand():When " + lt.name + " tried to nochannel " + args, e); + return "nochannel who for how long?"; + } + if (thnStore == null) { + return "They're not in this world."; + } + if (thnStore.privs >= lt.privs) { + thnStore.chatMessage(lt.name + " attempted to nochannel you."); + return "You do not have high enough privelages to nochannel them."; + } + if (duration > game.noChannelMax) { + duration = game.noChannelMax; + } + thnStore.chatMessage("You have been nochanneled for " + duration + " seconds."); + thnStore.noChannel = duration; + return "You have nochanneled " + thnStore.name + " for " + duration + " seconds"; + } + case "allowchannel": { + if (lt.privs <= 2) + return "Huh?"; + if (args == null) { + return "allowchannel who?"; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + LivingThing thnStore = game.getPlayer(args); + if (thnStore == null) { + return "They're not in this world."; + } + thnStore.chatMessage("Your nochannel status has been removed."); + thnStore.noChannel = 0; + return thnStore.name + "'s nochannel status has been removed."; + } + case "gecho": { + if (lt.privs <= 2) + return "Huh?"; + if (args == null) { + return "G-Echo what?"; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + game.chatMessage(args, "default"); + return null; + } + case "teleport": { + if (lt.privs <= 2) + return "Huh?"; + if (args == null) { + return "Teleport to where?"; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + int index = args.lastIndexOf('_'); + char charSep = ' '; + if (index != -1) { + charSep = '_'; + } + try { + int destX = Integer.parseInt(args.substring(0, args.lastIndexOf(charSep))); + int destY = Integer.parseInt(args.substring(args.lastIndexOf(charSep) + 1)); + if (lt.privs < 5 && destX >= game.map.getCols()) { + destX = game.map.getCols() - 1; + } + if (lt.privs < 5 && destY >= game.map.getRows()) { + destY = game.map.getRows() - 1; + } + if (destX < 0) { + destX = 0; + } + if (destY < 0) { + destY = 0; + } + lt.changeLocBypass(destX, destY); + } catch (Exception e) { + LivingThing thnStore = game.getPlayer(args); + if (thnStore == null) { + return "Teleport to where?"; + } else { + int destX = thnStore.x; + int destY = thnStore.y; + if (lt.privs < 5 && destX > 349) { + destX = 349; + } + if (lt.privs < 5 && destY > 349) { + destY = 349; + } + if (destX < 0) { + destX = 0; + } + if (destY < 0) { + destY = 0; + } + lt.changeLocBypass(destX, destY); + } + } + return null; + } + case "remove": { + if (lt.privs <= 2) + return "Huh?"; + if (args == null) { + return "remove what?"; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + DuskObject objStore = lt.getLocalObject(args); + if (objStore != null) { + if (objStore.isLivingThing()) { + LivingThing thnStore = (LivingThing) objStore; + if (thnStore.isMob()) { + thnStore.close(); + game.blnMobListChanged = true; + return "Object removed."; + } else { + return "You can't remove players/pets."; + } + } else if (objStore.isItem()) { + //game.vctItems.remove(objStore); + game.removeDuskObject(objStore); + return "Object removed."; + } else if (objStore.isSign()) { + //game.vctSigns.remove(objStore); + //game.blnSignListChanged = true; + game.removeDuskObject(objStore); + return "Object removed."; + } else if (objStore.isProp()) { + //game.vctProps.removeElement(objStore); + //game.blnPropListChanged = true; + game.removeDuskObject(objStore); + return "Object removed."; + } else if (objStore.isMerchant()) { + //game.vctMerchants.remove(objStore); + //game.blnMerchantListChanged = true; + game.removeDuskObject(objStore); + return "Object removed."; + } + } + return "You don't see that here."; + } + case "changeclan": { + if (lt.privs <= 2) + return "Huh?"; + if (args == null) { + return "ChangeClan who?"; + } + LivingThing thnStore = game.getPlayer(args.substring(0, args.indexOf(' '))); + if (thnStore == null) { + return "They're not in this world"; + } + if (cmdline.length() < thnStore.name.length() + 2) { + return "What clan?"; + } + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + args = args.substring(thnStore.name.length() + 1); + thnStore.clan = args; + if (thnStore.privs == 1) { + thnStore.privs = 0; + } + if (args.equals("none")) { + thnStore.chatMessage("You are now a member of no clan."); + } else { + thnStore.chatMessage("You are now a member of the " + args + " clan."); + } + return thnStore.name + " has been added to the " + args + " clan"; + } + case "madd": { + if (lt.privs <= 2) + return "Huh?"; + if (args == null) { + return "Madd what?"; + } + Merchant mrcStore = game.overMerchant(lt.x, lt.y); + if (mrcStore != null) { + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + mrcStore.items.add(args); + game.refreshEntities(lt); + } else { + if (game.overPlayerMerchant(lt.x, lt.y) != null) { + return "You cannot add items to a player's merchant this way."; + } + return "You are not on a merchant."; + } + return null; + } + case "mremove": { + if (lt.privs <= 2) + return "Huh?"; + if (args == null) { + return "Mremove what?"; + } + Merchant mrcStore = game.overMerchant(lt.x, lt.y); + if (mrcStore != null) { + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + mrcStore.items.remove(args); + game.refreshEntities(lt); + } else { + if (game.overPlayerMerchant(lt.x, lt.y) != null) { + return "You cannot remove items from a player's merchant this way."; + } + return "You are not on a merchant."; + } + return null; + } + case "whoip": { + if (lt.privs <= 2) + return "Huh?"; + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y); + + int nPlayers = game.playersByName.size(); + StringBuilder sb = new StringBuilder(); + + // TODO: this used to be atomic, does it need to be? + + for (LivingThing thnStore : game.playersByName.values()) { + boolean hidden = false; + if (thnStore.privs > 2) { + if (thnStore.hasCondition("invis")) { + hidden = true; + } + } + if (hidden && (lt.privs < thnStore.privs)) { + nPlayers--; + } + } + + lt.chatMessage("\tThere are " + nPlayers + " players online:"); + + for (LivingThing thnStore : game.playersByName.values()) { + boolean hidden = false; + boolean skip = false; + if (thnStore.privs > 2) { + if (thnStore.hasCondition("invis")) { + hidden = true; + } + } + if (hidden && (lt.privs < thnStore.privs)) { + skip = true; + } + if (!skip) { + String ip = thnStore.getAddress().toString(); + while (ip.length() < 34) { + ip += " "; + } + sb.setLength(0); + sb.append(" ").append(ip); + sb.append(thnStore.getCharacterPoints()).append("cp "); + if (thnStore.privs == 1) { + sb.append("{Clan Leader}"); + } else if (thnStore.privs == 3) { + sb.append("{God}"); + } else if (thnStore.privs == 4) { + sb.append("{High God}"); + } else if (thnStore.privs > 4) { + sb.append("{Master God}"); + } + if (hidden) { + sb.append("{hidden}"); + } + if (thnStore.noChannel != 0) { + sb.append("{nochanneled}"); + } + if (!thnStore.clan.equals("none")) { + sb.append("<" + thnStore.clan + "> "); + } + sb.append(thnStore.name + "\n"); + lt.chatMessage(sb.toString()); + } + } + + return null; + } + case "flee": { + if (lt.battle != null) { + lt.battle.addFirstCommand(lt, "flee"); + return null; + } else { + return "You're not fighting anyone"; + } + } + case "sleep": { + if (lt.isSleeping) { + return "You are already asleep"; + } + if (lt.battle != null) { + return "Not while you're fighting!"; + } + if (!(lt.getMaster() == null || lt.getMaster().isSleeping)) { + return "You can't sleep if you're following someone who's awake."; + } + lt.isSleeping = true; + lt.updateActions(); + game.removeDuskObject(lt); + game.addDuskObject(lt); + return "You go to sleep"; + } + case "wake": { + if (lt.isSleeping) { + lt.isSleeping = false; + lt.updateActions(); + game.removeDuskObject(lt); + game.addDuskObject(lt); + return "You wake up"; + } else { + return "You are already awake"; + } + } + case "change": { + if (lt.battle != null) { + return "Not while you're fighting!"; + } + if (args == null) { + return "Change what?"; + } + if (args.equals("race")) { + if (lt.getCharacterPoints() > game.changeRaceCpLimit) { + return "You can no longer change your race."; + } + lt.unloadRace(); + + // FIXME: I'm not sure why this needs to clear messages here. + + if (lt.isPet()) { + + lt.getMaster().halt(); + // lt.getMaster().stillThere(); // This puts something in the buffer + // lt.getMaster().thrConnection.sleep(750); // wait for it... + try { + // Empty out the BufferedReader for the answer + // while (lt.getMaster().instream.ready()) { + // lt.getMaster().instream.readLine(); + // } + } catch (Exception e) { + game.log.printError("parseCommand():Trying to empty ready buffer of pet's master for change race.", e); + } + } else { + lt.halt(); + // lt.stillThere(); // This puts something in the buffer + // lt.thrConnection.sleep(750); // wait for it... + try { + // Empty out the BufferedReader for the answer + // while (lt.instream.ready()) { + // lt.instream.readLine(); + // } + } catch (Exception e) { + game.log.printError("parseCommand():Trying to empty ready buffer of player for change race.", e); + } + } + lt.loadRace(); + if (lt.isPet()) { + lt.getMaster().proceed(); + lt.getMaster().updateStats(); + } else { + lt.proceed(); + } + game.removeDuskObject(lt); + game.addDuskObject(lt); + lt.updateStats(); + return "Your race has been changed."; + } + } + case "gossip": { + if (!lt.isPlayer()) { + return "Only players can use the gossip/clan/tell channels."; + } + if (lt.noChannel != 0) { + return "You can't do that when nochanneled."; + } + if (args == null) { + return "Gossip what?"; + } + if (args.length() > game.messagecap) { + return "That message was too long."; + } + if (!args.equals("")) { + long lTemp = lt.lastMessageStamp; + lt.lastMessageStamp = System.currentTimeMillis(); + if ((System.currentTimeMillis() - lTemp) < game.floodLimit) { + return "No flooding."; + } + String strPerson = lt.name; + if (lt.privs > 2 + && lt.hasCondition("invis") + && lt.hasCondition("invis2")) { + strPerson = "A god"; + } + game.chatMessage(strPerson + " gossips: " + args, lt.name); + } + return null; + } + case "clan": { + if (!lt.isPlayer() && !lt.isMob()) { + return "Only players can use the gossip/clan/tell channels."; + } + if (lt.clan.equals("none")) { + return "You're not in a clan. Use gossip instead"; + } + if (lt.noChannel != 0) { + return "You can't do that when nochanneled."; + } + if (args == null) { + return "Clan what?"; + } + if (args.length() > game.messagecap) { + return "That message was too long."; + } + if (!args.equals("")) { + long lTemp = lt.lastMessageStamp; + lt.lastMessageStamp = System.currentTimeMillis(); + if ((System.currentTimeMillis() - lTemp) < game.floodLimit) { + return "No flooding."; + } + game.chatMessage(lt.name + " clans: " + args, lt.clan, lt.name); + } + return null; + } + case "say": { + if (args == null) { + return "Say what?"; + } + if (lt.noChannel != 0) { + return "You can't do that when nochanneled."; + } + if (args.length() > game.messagecap) { + return "That message was too long."; + } + if (!args.equals("")) { + if (lt.isPet()) { + game.chatMessage("Pet " + lt.name + " says: " + args, lt.x, lt.y, lt.name); + } else if (lt.isMob()) { + game.chatMessage("Mob " + lt.name + " says: " + args, lt.x, lt.y, "default"); + } else { + long lTemp = lt.lastMessageStamp; + lt.lastMessageStamp = System.currentTimeMillis(); + if ((System.currentTimeMillis() - lTemp) < game.floodLimit) { + return "No flooding."; + } + String strPerson = lt.name; + if (lt.privs > 2 + && lt.hasCondition("invis") + && lt.hasCondition("invis2")) { + strPerson = "A god"; + } + game.chatMessage(strPerson + " says: " + args, lt.x, lt.y, lt.name); + } + } + return null; + } + case "emote": { + if (args == null) { + return "Emote what?"; + } + if (lt.noChannel != 0) { + return "You can't do that when nochanneled."; + } + if (args.length() > game.messagecap) { + return "That message was too long."; + } + if (!args.equals("")) { + long lTemp = lt.lastMessageStamp; + lt.lastMessageStamp = System.currentTimeMillis(); + if ((System.currentTimeMillis() - lTemp) < game.floodLimit) { + return "No flooding."; + } + String strPerson = lt.name; + if (lt.privs > 2 + && lt.hasCondition("invis") + && lt.hasCondition("invis2")) { + strPerson = "A god"; + } + game.chatMessage(strPerson + " " + args, lt.x, lt.y, lt.name); + } + return null; + } + case "tell": { + if (!lt.isPlayer()) { + return "Only players can use the gossip/clan/tell channels."; + } + if (args == null) { + return "Tell who what?"; + } + if (lt.noChannel != 0) { + return "You can't do that when nochanneled."; + } + StringTokenizer tknStore = new StringTokenizer(args, " "); + String strStore2 = null; + try { + strStore2 = tknStore.nextToken(); + } catch (Exception e) { + return "Tell who?"; + } + LivingThing thnStore = game.getPlayer(strStore2); + if (thnStore == null) { + return "They're not in this world."; + } + if (thnStore.privs > 2 + && thnStore.hasCondition("invis") + && thnStore.hasCondition("invis2")) { + return "They're not in this world."; + } + if (thnStore.name.equalsIgnoreCase(lt.name)) { + return "Talking to yourself is not a good sign."; + } + String strIgnoreName = thnStore.name.toLowerCase(); + if (lt.ignoreList.contains(strIgnoreName)) { + return "You can't do that while you are ignoring them."; + } + strIgnoreName = lt.name.toLowerCase(); + if (thnStore.ignoreList.contains(strIgnoreName)) { + return "They did not get the message, they are ignoring you."; + } + args = args.substring(strStore2.length(), args.length()).trim(); + if (args.length() > game.messagecap) { + return "That message was too long."; + } + if (args.length() == 0) { + return "Tell them what?"; + } + long lTemp = lt.lastMessageStamp; + lt.lastMessageStamp = System.currentTimeMillis(); + if ((System.currentTimeMillis() - lTemp) < game.floodLimit) { + return "No flooding."; + } + String strPerson = lt.name; + if (lt.privs > 2 + && lt.hasCondition("invis") + && lt.hasCondition("invis2")) { + strPerson = "A god"; + } + game.log.printMessage(Log.ALWAYS, lt.name + " tells " + thnStore.name + " : " + args); + thnStore.chatMessage(strPerson + " tells you: " + args); + return "You tell " + strStore2 + ": " + args; + } + case "who": { + int nPlayers = game.playersByName.size(); + StringBuilder sb = new StringBuilder(); + + // TOOD: originally this was atomic on stream + // although 'atomic' is wrong since nobody else was atomic on stream + + for (LivingThing thnStore : game.playersByName.values()) { + boolean hidden = false; + if (thnStore.privs > 2) { + if (thnStore.hasCondition("invis")) { + hidden = true; + } + } + if (hidden && (lt.privs < thnStore.privs)) { + nPlayers--; + } + if (lt.privs < 3 && !thnStore.isWorking) { + nPlayers--; + } + if (lt.privs < 3 && !thnStore.isReady) { + nPlayers--; + } + } + + lt.chatMessage("\tThere are " + nPlayers + " players online:"); + + for (LivingThing thnStore : game.playersByName.values()) { + boolean hidden = false; + boolean skip = false; + if (thnStore.privs > 2) { + if (thnStore.hasCondition("invis")) { + hidden = true; + } + } + if (hidden && (lt.privs < thnStore.privs)) { + skip = true; + } + if (lt.privs < 3 && !thnStore.isWorking) { + skip = true; + } + if (lt.privs < 3 && !thnStore.isReady) { + skip = true; + } + System.out.println(" user " + thnStore.name + " skip " + skip); + if (!skip) { + sb.setLength(0); + sb.append("\t"); + sb.append(thnStore.getCharacterPoints()); + sb.append("cp "); + if (lt.privs > 2 && !thnStore.isWorking) { + sb.append("{* Not Working *}"); + } + if (lt.privs > 2 && !thnStore.isReady) { + sb.append("{Entering the world}"); + } + if (lt.privs > 2 && !thnStore.isSaveable) { + sb.append("{Loading/Saving}"); + } + if (thnStore.privs == 1) { + sb.append("{Clan Leader}"); + } else if (thnStore.privs == 3) { + sb.append("{God}"); + } else if (thnStore.privs == 4) { + sb.append("{High God}"); + } else if (thnStore.privs > 4) { + sb.append("{Master God}"); + } + if (hidden) { + sb.append("{hidden}"); + } + if (thnStore.noChannel != 0) { + sb.append("{nochanneled}"); + } + if (thnStore.ignoreList.contains(lt.name.toLowerCase())) { + sb.append("{Ignoring you}"); + } + if (lt.ignoreList.contains(thnStore.name.toLowerCase())) { + sb.append("{Ignored}"); + } + if (!thnStore.clan.equals("none")) { + sb.append("<"); + sb.append(thnStore.clan); + sb.append("> "); + } + if (thnStore.title == null + || thnStore.title.equals("none")) { + sb.append(thnStore.name); + //sb.append("\n"); + } else { + sb.append(thnStore.name); + sb.append(" "); + sb.append(thnStore.title); + //sb.append("\n"); + } + lt.chatMessage(sb.toString()); + } + } + return "Who complete."; + } + case "kill": + case "attack": + case "a": { + if (args == null) { + return "Attack what?"; + } + if (lt.isSleeping) { + return "You can't do that while you're sleeping"; + } + DuskObject objStore = lt.getLocalObject(args); + if (objStore == null) { + return "You don't see that here."; + } + if (!objStore.isLivingThing()) { + return "You can't fight that."; + } + game.newBattle(lt, (LivingThing) objStore); + return null; + } + case "order": { + if (args == null) { + return "Order who to do what?"; + } + int intStore = args.indexOf(" "); + if (intStore == -1) { + return "Order them to do what?"; + } + DuskObject objStore = lt.getLocalObject(args.substring(0, intStore)); + if (objStore == null) { + return "You don't see that here."; + } + if (!objStore.isLivingThing()) { + return "You can't order that."; + } + LivingThing thnStore = (LivingThing) objStore; + if (thnStore.getCharmer() != lt) { + return "They don't take orders from you."; + } + args = args.substring(intStore + 1); + try { + thnStore.chatMessage(Commands.parseCommand(thnStore, game, args)); +// lt.chatMessage(Commands.parseCommand(thnStore, engGame, strArgs)); + } catch (Exception e) { + game.log.printError("parseCommand():" + thnStore.name + ", while trying to follow the following order: \"" + args + "\"", e); + } + return null; + } + + case "look": { + if (args == null) { + return "Look at what?"; + } + if (lt.isSleeping) { + return "You can't do that while you're sleeping"; + } + DuskObject objStore = lt.getLocalObject(args); + if (objStore != null) { + if (objStore.isLivingThing()) { + LivingThing thnStore = (LivingThing) objStore; + thnStore.chatMessage(lt.name + " is looking at you."); + lt.chatMessage(thnStore.name + " has " + thnStore.getCharacterPoints() + "cp and " + thnStore.hp + "/" + thnStore.maxhp + "hp."); + if (thnStore.description != null) { + lt.chatMessage("Their description is: " + thnStore.description); + } + final String[] formats = { + "They are wielding %s.", + "They are wearing %s on their arms.", + "They are wearing %s on their legs.", + "They are wearing %s on their torso.", + "They are wearing %s on their waist.", + "They are wearing %s on their neck.", + "They are wearing %s on their skull.", + "They are wearing %s on their eyes.", + "They are wearing %s on their hands." + }; + for (int i = 0; i < formats.length; i++) { + Item item = thnStore.wornItems.getWorn(i); + if (item != null) + lt.chatMessage(String.format(formats[i], item.description)); + } + return null; + } else if (objStore.isItem()) { + Item itmStore = (Item) objStore; + return "You see " + itmStore.description + "."; + } else if (objStore.isProp()) { + Prop prpStore = (Prop) objStore; + return "You see " + prpStore.description + "."; + } else if (objStore.isSign()) { + Sign sgnStore = (Sign) objStore; + return "The sign says " + sgnStore.strMessage + "."; + } else if (objStore.isPlayerMerchant()) { + lt.chatMessage("You see a merchant that sells"); + PlayerMerchant pmrStore = (PlayerMerchant) objStore; + boolean blnEmptyMerchant = true; + for (LinkedList list : pmrStore.vctItems.values()) { + Item item = (Item) list.element(); + cmdline = item.name; + String strSpacer = "\t"; + if (cmdline.length() < 11) { + strSpacer = "\t\t"; + } + lt.chatMessage("\t" + cmdline + strSpacer + item.description); + blnEmptyMerchant = false; + } + if (blnEmptyMerchant) { + lt.chatMessage("\tNothing at the moment."); + } + } else if (objStore.isMerchant()) { + lt.chatMessage("You see a merchant that sells"); + Merchant mrcStore = (Merchant) objStore; + boolean blnEmptyMerchant = true; + for (String item : mrcStore.items) { + Item itmStore = game.getItem(item); + if (itmStore != null) { + String strSpacer = "\t"; + if (item.length() < 11) { + strSpacer = "\t\t"; + } + lt.chatMessage("\t" + item + strSpacer + itmStore.description); + blnEmptyMerchant = false; + } else { + if (item.equals("pet")) { + lt.chatMessage("\tpets."); + blnEmptyMerchant = false; + } else { + item = item.substring(6, item.length()); + lt.chatMessage("\ttraining in " + item); + blnEmptyMerchant = false; + } + } + } + if (blnEmptyMerchant) { + lt.chatMessage("\tNothing."); + } + return null; + } + } + return "You don't see that here."; + } + + case "inv": + case "inventory": { + final String[] formats = { + "Wielded: %s", + "Arms: %s", + "Legs: %s", + "Torso: %s", + "Waist: %s", + "Neck: %s", + "Skull: %s", + "Eyes: %s", + "Hands: %s"}; + lt.chatMessage("-Worn-"); + for (int i = 0; i < formats.length; i++) { + Item item = lt.wornItems.getWorn(i); + if (item != null) + lt.chatMessage(String.format(formats[i], item.description)); + } + lt.chatMessage("-Inventory-:"); + for (LinkedList list : lt.itemList.values()) { + if (!list.isEmpty()) { + Item item = (Item) list.element(); + lt.chatMessage(list.size() + " " + item.name); + } + } + return null; + } + + case "help": { + File file; + String title; + + // FIXME: was atomic + if (args == null) { + file = new File("help"); + title = "Help"; + } else { + if (args.indexOf("..") != -1) { + return "There is no help on that subject"; + } + file = new File("helpFiles/" + args); + title = "Help on " + args; + } + + try (RandomAccessFile helpFile = new RandomAccessFile(file, "r")) { + lt.chatMessage(title); + while ((cmdline = helpFile.readLine()) != null) { + lt.chatMessage(cmdline); + } + } catch (IOException e) { + game.log.printError("parseCommand():When " + lt.name + " tried to get help on " + args, e); + return "There is no help on that subject"; + } + return null; + } + + case "get": { + if (args == null) { + return "Get what?"; + } + DuskObject objStore = lt.getLocalObject(args); + if (objStore == null) { + return "You don't see that here."; + } + if (!objStore.isItem()) { + return "You can't get that."; + } + Item itmStore = (Item) objStore; + if (Math.abs(lt.x - itmStore.x) + Math.abs(lt.y - itmStore.y) < 2) { + if (lt.privs > 2) { + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":gets " + args + ":" + lt.x + "," + lt.y); + } + lt.itemList.addElement(itmStore); + lt.updateItems(); + game.removeDuskObject(itmStore); + } else { + return "That's too far away."; + } + itmStore.onGetItem(game, lt); + return null; + } + + case "drop": { + if (args == null) { + return "Drop what?"; + } + int intDot = args.indexOf("."); + int intNumToDrop = 1; + if (intDot != -1) { + try { + intNumToDrop = Integer.parseInt(args.substring(0, intDot)); + } catch (NumberFormatException e) { + intNumToDrop = 1; + } + } + Item itmStore = lt.getItem(args); + if (itmStore != null) { + String strMessage = "You drop " + itmStore.name + "."; + if (intNumToDrop > 1) { + strMessage = "You drop " + intNumToDrop + " " + itmStore.name + "."; + } + if (itmStore.intCost == 0) { + strMessage = "A " + itmStore.name + " vanishes into thin air."; + if (intNumToDrop > 1) { + strMessage = intNumToDrop + " " + itmStore.name + " vanish into thin air."; + } + } + if (lt.privs > 2) { + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":drops " + args + ":" + lt.x + "," + lt.y); + } + for (int i = 0; i < intNumToDrop; i++) { + itmStore = lt.getItemAndRemove(itmStore.name); + itmStore.x = lt.x; + itmStore.y = lt.y; + if (itmStore.intCost != 0) { + //game.vctItems.add(itmStore); + game.addDuskObject(itmStore); + lt.updateItems(); + } + itmStore.onDropItem(game, lt); + } + return strMessage; + } + return "You don't have that."; + } + + case "use": { + if (args == null) { + return "Use what?"; + } + if (lt.battle == null) { + lt.useItem(args, -1); + } else { + lt.battle.addCommand(lt, "use " + args); + } + return null; + } + case "eat": { + if (args == null) { + return "Eat what?"; + } + if (lt.battle == null) { + lt.useItem(args, Item.FOOD); + } else { + lt.battle.addCommand(lt, "eat " + args); + } + return null; + } + case "drink": { + if (args == null) { + return "Drink what?"; + } + if (lt.battle == null) { + lt.useItem(args, Item.DRINK); + } else { + lt.battle.addCommand(lt, "drink " + args); + } + return null; + } + + case "give": { + if (args == null) { + return "Give who what?"; + } + StringTokenizer tknStore = new StringTokenizer(args, " "); + String strStore2 = null; + try { + strStore2 = tknStore.nextToken(); + } catch (Exception e) { + return "Give who what?"; + } + DuskObject objStore = lt.getLocalObject(strStore2); + if (objStore == null) { + return "You don't see them here."; + } + if (!objStore.isLivingThing()) { + return "You can't give to that."; + } + LivingThing thnStore = (LivingThing) objStore; + if ((lt.privs < 3) && (Math.abs(thnStore.x - lt.x) + Math.abs(thnStore.y - lt.y) > 1)) { + return "They're too far away."; + } + args = args.substring(strStore2.length() + 1); + if (lt.privs > 2) { + game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":gives " + args + " to " + strStore2 + ":" + lt.x + "," + lt.y); + } + if (args.startsWith("gp")) { + args = args.substring(3); + try { + int intStore = Integer.parseInt(args); + if (intStore < 0) { + return "You can't give negative money!"; + } + if (intStore <= lt.cash) { + lt.cash -= intStore; + thnStore.cash += intStore; + lt.updateStats(); + thnStore.updateStats(); + thnStore.chatMessage(lt.name + " gives you " + intStore + "gp."); + return "You give " + thnStore.name + " " + intStore + "gp."; + } else { + lt.chatMessage("You don't have that much gp"); + } + } catch (Exception e) { + return "That is not a valid amount of gp to give."; + } + } + int intDot = args.indexOf("."); + int intNumToGive = 1; + if (intDot != -1) { + try { + intNumToGive = Integer.parseInt(args.substring(0, intDot)); + } catch (NumberFormatException e) { + intNumToGive = 1; + } + } + Item itmStore = lt.getItem(args); + if (itmStore != null) { + String strMessage = lt.name + " gives you "; + String strMessage2 = "You give " + thnStore.name + " "; + if (intNumToGive > 1) { + strMessage += intNumToGive + " "; + strMessage2 += intNumToGive + " "; + } + strMessage += itmStore.name + "."; + strMessage2 += itmStore.name + "."; + cmdline = itmStore.name; + + while (intNumToGive > 0) { + itmStore = lt.getItemAndRemove(cmdline); + thnStore.itemList.addElement(itmStore); + intNumToGive--; + + itmStore.onDropItem(game, lt); + itmStore.onGetItem(game, thnStore); + } + + thnStore.chatMessage(strMessage); + thnStore.updateItems(); + lt.updateItems(); + return strMessage2; + } + return "You don't have that."; + } + + case "buy": { + if (args == null) { + return "Buy what?"; + } + int quantity; + try { + int i = args.indexOf(" "); + quantity = Integer.parseInt(args.substring(0, i)); + args = args.substring(i + 1); + } catch (Exception e) { + return "How many of what do you want to buy?"; + } + if (quantity > 100) { + quantity = 100; + } else if (quantity < 1) { + return "You can't buy less than one of something."; + } + PlayerMerchant pmrStore = game.overPlayerMerchant(lt.x, lt.y); + if (pmrStore != null) { + long numItem = pmrStore.contains(args); + if (numItem > 0) { + Item itmStore = game.getItem(args); + if (itmStore != null) { + if (quantity > numItem) { + return "This merchant does not have that many."; + } + int intCost = (itmStore.intCost * 3) / 4; + if (lt.name.equalsIgnoreCase(pmrStore.strOwner)) { + intCost = 0; + } + if (intCost * quantity > lt.cash) { + return "You can't afford that"; + } else { + lt.cash -= intCost * quantity; + pmrStore.cash += intCost * quantity; + itmStore = pmrStore.remove(args); + lt.itemList.addElement(itmStore); + for (int i = 1; i < quantity; i++) { + itmStore = pmrStore.remove(args); + lt.itemList.addElement(itmStore); + } + lt.updateItems(); + lt.updateStats(); + } + } + } + } + + Merchant mrcStore = game.overMerchant(lt.x, lt.y); + if (mrcStore == null) { + return "Buy from whom?"; + } + if (lt.getFollowing() != null && lt.getFollowing().isPet()) { + if (args.startsWith(lt.getFollowing().name + ":")) { + args = args.substring(lt.getFollowing().name.length() + 1); + if (mrcStore.contains(args)) { + if (args.startsWith("train:")) { + args = args.substring(6); + mrcStore.train(args, quantity, lt.getFollowing()); + lt.updateStats(); + } + } + return null; + } + } + if (mrcStore.contains(args)) { + if (args.startsWith("train:")) { + args = args.substring(6); + mrcStore.train(args, quantity, lt); + lt.updateStats(); + } else { + if (args.startsWith("pet")) { + mrcStore.pet(lt); + lt.updateStats(); + } else { + Item itmStore = game.getItem(args); + if (itmStore != null) { + if (itmStore.intCost * quantity > lt.cash) { + return "You can't afford that"; + } else { + lt.cash -= itmStore.intCost * quantity; + lt.itemList.addElement(itmStore); + for (int i = 1; i < quantity; i++) { + lt.itemList.addElement(game.getItem(args)); + } + lt.updateItems(); + lt.updateStats(); + } + } + } + } + } + return null; + } + + case "sell": { + if (args == null) { + return "Sell what?"; + } + + PlayerMerchant pmrStore = game.overPlayerMerchant(lt.x, lt.y); + if (pmrStore != null) { + if (lt.name.equalsIgnoreCase(pmrStore.strOwner)) { + int quantity = 1; + try { + int i = args.indexOf(" "); + quantity = Integer.parseInt(args.substring(0, i)); + args = args.substring(i + 1); + } catch (Exception e) { + return "How many of what do you want to sell?"; + } + Item itmStore = lt.getItem(args); + for (int i = 0; i < quantity; i++) { + itmStore = lt.getItemAndRemove(args); + if (itmStore != null) { + itmStore.onDropItem(game, lt); + pmrStore.add(itmStore); + lt.isSaveNeeded = true; + } else { + i = quantity; + } + } + lt.updateItems(); + lt.updateStats(); + return null; + } + return "You cannot sell items to this merchant."; + } + + Merchant mrcStore = game.overMerchant(lt.x, lt.y); + if (mrcStore == null) { + return "Sell that to whom?"; + } + int quantity = 1; + try { + int i = args.indexOf(" "); + quantity = Integer.parseInt(args.substring(0, i)); + args = args.substring(i + 1); + } catch (Exception e) { + return "How many of what do you want to sell?"; + } + if (quantity > 100) { + quantity = 100; + } + Item itmStore = lt.getItem(args); + for (int i = 0; i < quantity; i++) { + itmStore = lt.getItemAndRemove(args); + if (itmStore != null) { + itmStore.onDropItem(game, lt); + lt.cash += (itmStore.intCost / 2); + lt.isSaveNeeded = true; + } else { + i = quantity; + } + } + lt.updateItems(); + lt.updateStats(); + return null; + } + + case "cast": { + if (args == null) { + return "Cast what?"; + } + if (lt.battle == null) { + lt.castSpell(args); + } else { + lt.battle.addCommand(lt, "cast " + args);; + } + return null; + } + + case "follow": { + if (args == null) { + return "Follow who?"; + } + if (lt.isSleeping) { + return "You can't do that while you're sleeping"; + } + DuskObject objStore = lt.getLocalObject(args); + if (objStore == null) { + return "You don't see that here."; + } + if (objStore.isLivingThing()) { + LivingThing thnStore = (LivingThing) objStore; + if (lt.getMaster() != null && thnStore != lt.getMaster()) { + if (lt.isPet()) { + return "You can only follow your owner."; + } + return "You're already following someone. Leave them first."; + } + if (Math.abs(lt.x - thnStore.x) + Math.abs(lt.y - thnStore.y) > 1) { + return "They're too far away."; + } + if (thnStore == lt) { + return "You can't follow yourself."; + } + if (!thnStore.isPlayer() && !lt.isMob()) { + return "You can only follow players."; + } + if (thnStore.noFollow || (thnStore.isPet() && thnStore.getMaster().noFollow)) { + return "They won't let you follow them."; + } + if (lt.isPet()) { + thnStore.setFollowing(lt); + lt.setMaster(thnStore); + thnStore.updateStats(); + lt.updateStats(); + return "You are now following " + lt.getMaster().name + "."; + } + LivingThing thnStore2 = thnStore; + while (thnStore2 != null) { + if (lt == thnStore2) { + return "You're already in that group."; + } + thnStore2 = thnStore2.getMaster(); + } + thnStore.chatMessage("You are now being followed by " + lt.name + "."); + while (thnStore.getFollowing() != null) { + thnStore = thnStore.getFollowing(); + if (thnStore.isPlayer()) { + thnStore.chatMessage("You are now being followed by " + lt.name + "."); + } + } + thnStore.setFollowing(lt); + lt.setMaster(thnStore); + thnStore.updateStats(); + lt.updateStats(); + return "You are now following " + lt.getMaster().name + "."; + } + return "That's not something you can follow."; + } + + case "unfollow": { + if (args == null) { + return "Unfollow who?"; + } + LivingThing thnStore = lt.getFollowing(); + if (thnStore != null && thnStore.isPet()) { + if (thnStore.name.equalsIgnoreCase(args)) { + lt.halt(); + lt.chatMessage("Do you really want to permanently erase your pet?"); + try { + if (lt.instream.readLine().equalsIgnoreCase("yes")) { + lt.getFollowing().close(); + File deleteme = new File("pets/" + lt.name.toLowerCase()); + deleteme.delete(); + deleteme = new File("pets/" + lt.name.toLowerCase() + ".backup"); + deleteme.delete(); + lt.getFollowing().close(); + lt.setFollowing(lt.getFollowing().getFollowing()); + lt.proceed(); + return "Your pet has been erased."; + } + } catch (Exception e) { + game.log.printError("parseCommand():While unfollowing pet for " + lt.name, e); + } + lt.proceed(); + return null; + } + thnStore = thnStore.getFollowing(); + } + while (thnStore != null) { + if (thnStore.name.equalsIgnoreCase(args)) { + if (thnStore.isPet()) { + thnStore = thnStore.getMaster(); + } + thnStore.removeFromGroup(); + return null; + } + thnStore = thnStore.getFollowing(); + } + return "They're not following you."; + } + + case "stay": { + if (lt.isPet()) { + lt.removeFromGroup(); + return Commands.parseCommand(lt, game, "emote sits down to await " + lt.getMaster().name + "'s return."); + } + return (Commands.parseCommand(lt, game, "emote stays like a good little puppy.")); + } + + case "leave": { + if (lt.isPet()) { + return "You cannot leave your master unless he unfollows you."; + } + lt.removeFromGroup(); + return "You are now on your own."; + } + + case "unclan": { + if (lt.clan.equals("none")) { + return "You're not in a clan."; + } + if (lt.battle != null) { + return "Wait until you're done battling."; + } + try { + lt.halt(); + lt.chatMessage("Are you sure you want to drop out of your clan? If so type yes."); + if (lt.instream.readLine().equalsIgnoreCase("yes")) { + lt.clan = "none"; + if (lt.privs == 1) { + lt.privs = 0; + } + lt.proceed(); + game.removeDuskObject(lt); + game.addDuskObject(lt); + return "You have been removed from your clan."; + } + } catch (Exception e) { + game.log.printError("parseCommand():While " + lt.name + " was trying to dropout of their clan", e); + } + lt.proceed(); + return null; + } + + case "description": { + if (args == null) { + lt.description = null; + return "Your description has been removed."; + } + lt.description = args; + return "Your description has been set to:" + lt.description; + } + + case "title": { + if (!lt.isPlayer()) { + return "Only players may have titles."; + } + if (args == null) { + lt.title = null; + return "Your title has been removed."; + } + lt.title = args; + if (lt.title.length() > game.titlecap) { + lt.title = lt.title.substring(0, game.titlecap); + } + return "Your title has been set to:" + lt.title; + } + + case "password": { + if (!lt.isPlayer()) { + return "Only players can change their password."; + } + try { + lt.halt(); + lt.chatMessage("Enter your current password."); + String strOldPass = lt.instream.readLine(); + if (!strOldPass.equals(lt.password)) { + lt.proceed(); + return "Sorry, that is not your password."; + } + lt.chatMessage("Enter a new password."); + String strNewPass = lt.instream.readLine(); + lt.chatMessage("Repeat that password."); + String strNewPassRepeat = lt.instream.readLine(); + if (strNewPass == null) { + lt.proceed(); + return "Not a valid password."; + } + if (!strNewPass.equals(strNewPassRepeat)) { + lt.proceed(); + return "Sorry, those passwords do not match."; + } + lt.password = strNewPass; + lt.proceed(); + return "Your password has now been changed."; + } catch (Exception e) { + game.log.printError("parseCommand():While " + lt.name + " was changing their password", e); + } + lt.proceed(); + } + + case "wear": { + if (args == null) { + return "Wear what?"; + } + // FIXME: this should go on livingthing, but the interaction flow is messy + LinkedList qStore = lt.itemList.get(args); + if (qStore != null) { + Item itmStore = (Item) qStore.element(); + int where = -1; + + switch (itmStore.intType) { + case (1): { + where = Equipment.WIELD; + break; + } + case (2): + where = itmStore.intKind + Equipment.ARMS; + break; + default: + return "You can't wear that"; + } + + Item old = lt.wornItems.wear(where, itmStore); + if (old != null) { + lt.itemList.addElement(old); + lt.onUnwear(old); + } + lt.onWear(itmStore); + + lt.itemList.removeElement(itmStore.name); + if (lt.isPet()) { + lt.getMaster().updateStats(); + } + if (lt.isPlayer()) { + lt.updateStats(); + } + lt.updateEquipment(); + lt.updateItems(); + return null; + } + return "You don't have that"; + } + case "unwear": { + if (args == null) { + return "Unwear what?"; + } + lt.unWear(args); + return null; + } + case "rement": { + if (args == null) { + return null; + } + long lngID = Long.parseLong(args); + lt.removeEntity(lngID); + return null; + } + case "audio": { + if (args == null) { + if (lt.audioon) { + return "Your audio is turned on."; + } + return "Your audio is turned off."; + } else if (args.equalsIgnoreCase("off")) { + lt.audioon = false; + return "Your audio has been turned off."; + } else if (args.equalsIgnoreCase("on")) { + lt.audioon = true; + return "Your audio has been turned on."; + } + } + case "color": { + if (args == null) { + if (lt.coloron) { + return "Your color is turned on."; + } + return "Your color is turned off."; + } else if (args.equalsIgnoreCase("off")) { + lt.coloron = false; + return "Your color has been turned off."; + } else if (args.equalsIgnoreCase("on")) { + lt.coloron = true; + return "Your color has been turned on."; + } + } + case "highlight": { + if (args == null) { + if (lt.highlight) { + return "Highlighting of enemies in battle is turned on."; + } + return "Highlighting of enemies in battle is turned off."; + } else if (args.equalsIgnoreCase("off")) { + lt.highlight = false; + lt.clearFlags(); + return "Highlighting of enemies in battle has been turned off."; + } else if (args.equalsIgnoreCase("on")) { + lt.highlight = true; + return "Highlighting of enemies in battle has been turned on."; + } + } + case "popup": { + // FIXME: TBD + if (args == null) { + if (lt.popup) { + return "You have popup windows turned on."; + } + return "You have popup windows turned off."; + } else if (args.equalsIgnoreCase("off")) { + lt.popup = false; + return "You have turned popup windows off."; + } else if (args.equalsIgnoreCase("on")) { + lt.popup = true; + return "You have turned popup windows on."; + } + } + case "nofollow": { + if (args == null) { + if (lt.noFollow) { + return "You are not allowed to be followed."; + } + return "You can be followed."; + } else if (args.equalsIgnoreCase("off")) { + lt.noFollow = false; + return "You can now be followed."; + } else if (args.equalsIgnoreCase("on")) { + lt.noFollow = true; + return "You can no longer be followed."; + } + } + case "ignore": { + if (args == null) { + return "Ignore who?"; + } + LivingThing thnStore = game.getPlayer(args); + if (thnStore == null) { + return "They're not in this world."; + } + String strIgnoreName = thnStore.name.toLowerCase(); + if (lt.name.equalsIgnoreCase(strIgnoreName)) { + return "Trying to ignore yourself is not a good sign."; + } + if (thnStore.privs > 2) { + return "You cannot ignore a god."; + } + if (!lt.ignoreList.contains(strIgnoreName)) { + lt.ignoreList.add(strIgnoreName); + } else { + return "You are already ignoring them."; + } + return "You are now ignoring " + strIgnoreName; + } + case "unignore": { + if (args == null) { + return "UnIgnore who?"; + } + String strIgnoreName = args.toLowerCase(); + if (strIgnoreName == "all") { + lt.ignoreList.clear(); + return "You are no longer ignoring anyone."; + } + if (lt.ignoreList.contains(strIgnoreName)) { + lt.ignoreList.remove(strIgnoreName); + } else { + return "You are not ignoring them."; + } + return "You are no longer ignoring " + strIgnoreName; + } + case "appletimages": { + lt.updateAppletImages(); + return null; + } + case "applicationimages": { + lt.updateApplicationImages(); + return null; + } + case "notdead": { + return null; + } + case "quit": + case "logout": { + if (lt.battle == null) { + lt.close(); + return null; + } + return "You cannot quit in the middle of a fight."; + } + } + if (!blnFoundScriptedCommand) { + return "huh?"; + } + return null; + } +} diff --git a/DuskServer/src/duskz/server/Condition.java b/DuskServer/src/duskz/server/Condition.java new file mode 100644 index 0000000..76071e8 --- /dev/null +++ b/DuskServer/src/duskz/server/Condition.java @@ -0,0 +1,110 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - modernised java, clean up script commands. + */ +package duskz.server; + +import duskz.server.entity.LivingThing; +import java.io.IOException; +import java.io.RandomAccessFile; + +/** + * Condition describes a temporary or permanent effect. + * + * @author Tom Weingarten + */ +public class Condition { + + public final String name; + public int duration = -1, //number of occurances till end. -1 = infinity + occurance = 1, //number of ticks per occurance + ticksPast = 0; //how many ticks since last occurance + public boolean display = true; //should the player be able to see the condition + // Script names + private String onStartScript = null, + onOccuranceScript = null, + onEndScript = null; + + public Condition(String name) { + this.name = name; + } + + public static Condition getCondition(String name) throws IOException { + String s; + Condition store = new Condition(name); + + try (RandomAccessFile rafConditionDef = new RandomAccessFile("defConditions/" + name.toLowerCase(), "r")) { + s = rafConditionDef.readLine(); + while (!(s == null || s.equals("."))) { + switch (s.toLowerCase()) { + case "duration": + store.duration = Integer.parseInt(rafConditionDef.readLine()); + break; + case "occurance": + store.occurance = Integer.parseInt(rafConditionDef.readLine()); + break; + case "onstart": + store.onStartScript = rafConditionDef.readLine(); + break; + case "onoccurance": + store.onOccuranceScript = rafConditionDef.readLine(); + break; + case "onend": + store.onEndScript = rafConditionDef.readLine(); + break; + case "nodisplay": + store.display = false; + break; + } + s = rafConditionDef.readLine(); + } + } + return store; + } + + void runScript(DuskEngine engGame, LivingThing trigger, String script) { + if (script != null) { + try { + Script scrStore = new Script("scripts/" + script, engGame, false); + scrStore.varVariables.addVariable("trigger", trigger); + scrStore.varVariables.addVariable("condition", name); + scrStore.runScript(); + scrStore.close(); + } catch (Exception e) { + engGame.log.printError("Condition.onStart() for condition \"" + name + "\"", e); + } + } + } + + public void onStart(DuskEngine engGame, LivingThing trigger) { + runScript(engGame, trigger, onStartScript); + } + + public void onOccurance(DuskEngine engGame, LivingThing trigger) { + runScript(engGame, trigger, onOccuranceScript); + } + + public void onEnd(DuskEngine engGame, LivingThing trigger) { + runScript(engGame, trigger, onEndScript); + } +} \ No newline at end of file diff --git a/DuskServer/src/duskz/server/Config.java b/DuskServer/src/duskz/server/Config.java new file mode 100644 index 0000000..75decba --- /dev/null +++ b/DuskServer/src/duskz/server/Config.java @@ -0,0 +1,70 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.server; + +import java.util.Properties; +import java.io.FileInputStream; + +class Config extends Properties { + + DuskEngine engGame; + + public Config(DuskEngine inGame, String strFileName) { + engGame = inGame; + try { + load(new FileInputStream(strFileName)); + } catch (Exception e) { + engGame.log.printError("Config::Config(String) - Error loading preferences.", e); + } + } + + public String getString(String strKey, String strDefault) { + String strReturn = getProperty(strKey, strDefault); + engGame.log.printMessage(Log.VERBOSE, strKey + " = " + strReturn); + return strReturn; + } + + public int getInt(String strKey, int intDefault) { + int intReturn = Integer.parseInt(getProperty(strKey, String.valueOf(intDefault))); + engGame.log.printMessage(Log.VERBOSE, strKey + " = " + intReturn); + return intReturn; + } + + public boolean getBoolean(String strKey, boolean blnDefault) { + boolean blnReturn = (Boolean.valueOf(getProperty(strKey, String.valueOf(blnDefault))).booleanValue()); + engGame.log.printMessage(Log.VERBOSE, strKey + " = " + blnReturn); + return blnReturn; + } + + public long getLong(String strKey, long lngDefault) { + long lngReturn = Long.parseLong(getProperty(strKey, String.valueOf(lngDefault))); + engGame.log.printMessage(Log.VERBOSE, strKey + " = " + lngReturn); + return lngReturn; + } + + public double getDouble(String strKey, double dblDefault) { + double dblReturn = Double.parseDouble(getProperty(strKey, String.valueOf(dblDefault))); + engGame.log.printMessage(Log.VERBOSE, strKey + " = " + dblReturn); + return dblReturn; + } +} diff --git a/DuskServer/src/duskz/server/Constants.java b/DuskServer/src/duskz/server/Constants.java new file mode 100644 index 0000000..0d4e9d8 --- /dev/null +++ b/DuskServer/src/duskz/server/Constants.java @@ -0,0 +1,61 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.server; + +/** + * This class defines all constants used throughout the code system. + * + * @Author Lee Templeton (Lone Wolf) + */ +//TODO: Move all commonly used variables to here +public interface Constants { + // DUSKENGINE Section + + static final int MIN = 0; + static final int MAX = 1; + // DUSKOBJECT Section + static final int NUMBER = 0; + static final int STRING = 1; + static final int LIVING_THING = 2; + static final int ITEM = 3; + // ATTRIBUTE Section + static final int MAIN = 0; + static final int SECONDARY = 1; + static final int BONUS = 2; + static final int ALL = 100; + // ITEM Section + static final int ORDINARY_ITEM = 0; + static final int WEAPON = 1; + static final int ARMOR = 2; + static final int FOOD = 3; + static final int DRINK = 4; + static final int RANGED = 5; + static final int CONTAINER = 6; + // CAP Section + static final int STAT_CAP = 100; + static final int SKILL_CAP = 100; + static final int MAX_CAP = 200; + // Strings + static final String CONFIG_FILE_NAME = "prefs"; + static final String CONFIG_FILE_DIR = "conf/"; +} diff --git a/DuskServer/src/duskz/server/DuskEngine.java b/DuskServer/src/duskz/server/DuskEngine.java new file mode 100644 index 0000000..cd438ef --- /dev/null +++ b/DuskServer/src/duskz/server/DuskEngine.java @@ -0,0 +1,2045 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - general cleanup, modernisation, refactoring, + * abstracting, fixing synchronisation issues. + */ +package duskz.server; + +import duskz.protocol.MessageType; +import duskz.server.entity.TileMap; +import duskz.server.entity.Mob; +import duskz.server.entity.Merchant; +import duskz.server.entity.Sign; +import duskz.server.entity.Item; +import duskz.server.entity.Prop; +import duskz.server.entity.DuskObject; +import duskz.server.entity.PlayerMerchant; +import duskz.server.entity.LivingThing; +import duskz.protocol.ServerMessage; +import duskz.protocol.TransactionItem; +import java.io.*; +import java.io.PrintStream; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.StringTokenizer; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; +import java.util.Properties; + +/** + * DuskEngine is the center of the Dusk game. + * + * @author Tom Weingarten + */ +public class DuskEngine implements Runnable { + //Logger + + public Log log; + //Prefs: + public int port = 7400, + trackerport = 7401, + maxconnections = 0, + petcost = 100, + merchantimage, + signimage, + battlesound = -1, + mobrespawnspeed, + viewrange = 5, + oldviewrange = 0, + mapsize = 11, + titlecap = 0, + noChannelMax = 600, // defalut 600=10 minutes + namecap = 20, //default 20 + messagecap = 120, //default 120 + changeRaceCpLimit = 100, //default 100cp + logLevel = Log.ERROR, + traincost = 100, + expgainmod = 10; + double gplosemod = 1D / 16D, //default 1/16 + explosemod = 1D / 16D, + gpfleemod = 1D / 64D, //default 1/64 + expfleemod = 1D / 64D; + public long lngMobTicks = 1000, //default 1000 milliseconds(1 second) per tick + lngPlayerTicks = 250, //default 250 milliseconds(1/4 second) per tick + floodLimit = 1000; //default 1000 milliseconds(1 second) btw messages + public String trackername = "Just Some World.", + site = "none", + strRCAddress, + strRCName, + strLogFile = null, + strMusicAddress; + public boolean blnMusic = false, + blnLineOfSight = false, + blnAI = false, + blnIPF = false, + tracker = true, + blnSavingGame = false, + blnShuttingDown = false; + // FIXME: Not public + public Script scrCanSeeLivingThing = null, + scrCanMoveThroughLivingThing = null, + scrCanAttack = null, + scrOnStart = null, + scrOnDeath = null, + scrOnLogOut = null; + //End Prefs + public final static String version = "2.6.2.W42"; + public final Date datStart = new Date(System.currentTimeMillis()); + //public int MapRows, + // MapColumns; + //public short shrMap[][]; + //protected short shrMapOwnerPrivs[][]; + //protected int intMapOwnerID[][]; + //protected Config IDtoName; + public TileMap map; + final public List battleList = new ArrayList<>(); + // Indices of various Entities + final public HashSet merchantList = new HashSet<>(); + /** + * List of mobs in game. + * access must be synchronised. + */ + final public List mobList = new ArrayList<>(); + // TODO: although this is maintained, nobody access it + final public List itemList = new ArrayList<>(); + final public List signList = new ArrayList<>(); + final public List propList = new ArrayList<>(); + final public List petList = new ArrayList<>(); + // tile actions, keyed by tile id + final public HashMap tileCanSee = new HashMap<>(); + final public HashMap tileCanMove = new HashMap<>(); + final public HashMap tileAction = new HashMap<>(); + // + final public HashMap factionMap = new HashMap<>(); + final public HashMap spellGroupList = new HashMap<>(); + /** + * Automatic temporary bans + */ + final private HashMap bannedIPMap = new HashMap<>(); + final public HashSet checkConditionList = new HashSet<>(); + public final HashMap playersByName = new HashMap<>(); + VariableSet varVariables; + public VariableSet varIP; + private long nextid = 0; + boolean blnVariableListChanged = false, + blnMapHasChanged = false, + blnMobListChanged = false, + blnSignListChanged = false, + blnMerchantListChanged = false, + blnPropListChanged = false; + + public DuskEngine() { + RandomAccessFile rafFile = null; + varVariables = new VariableSet(); + varIP = new VariableSet(); + log = new Log(System.out); + + try { + int x = 0, + y = 0; + String strStore; + + loadPrefs(); + + // Load Map + File newmap = new File("shortmapx"); + if (newmap.exists()) { + log.printMessage(Log.INFO, "Loading Map..."); + map = TileMap.loadMapX(newmap); + log.printMessage(Log.VERBOSE, map.getCols() + "/" + map.getRows()); + } else { + newmap = new File("shortmap"); + if (newmap.exists()) { + log.printMessage(Log.INFO, "Loading Map..."); + map = TileMap.loadMap(newmap, TileMap.FORMAT_SHORT); + log.printMessage(Log.VERBOSE, map.getCols() + "/" + map.getRows()); + } else { + log.printMessage(Log.INFO, "Converting old map to new short format..."); + map = TileMap.loadMap(newmap, TileMap.FORMAT_BYTE); + log.printMessage(Log.VERBOSE, map.getCols() + "/" + map.getRows()); + } + } + + log.printMessage(Log.INFO, "Map Loaded..."); + + // Loading Map tile priviledges + /* + File mapprivs = new File("shortmap.privs"); + if (mapprivs.exists()) { + rafFile = new RandomAccessFile("shortmap.privs", "r"); + log.printMessage(Log.INFO, "Loading Map Tile Privs..."); + int PrivsColumns = rafFile.readInt(); + int PrivsRows = rafFile.readInt(); + if (PrivsColumns != MapColumns || PrivsRows != MapRows) { + log.printMessage(Log.ERROR, "Map privs array size (" + PrivsColumns + "/" + PrivsRows + ") does not match map size (" + MapColumns + "/" + MapRows + ")"); + } + log.printMessage(Log.ALWAYS, PrivsColumns + "/" + PrivsRows); + shrMapOwnerPrivs = new short[PrivsColumns][PrivsRows]; + for (x = 0; x < PrivsColumns; x++) { + for (y = 0; y < PrivsRows; y++) { + shrMapOwnerPrivs[x][y] = rafFile.readShort(); + } + } + rafFile.close(); + } + + // Loading Map tile ownership + File mapowners = new File("shortmap.owner"); + File ownerlist = new File("tile_owner_list"); + if (mapowners.exists() && ownerlist.exists()) { + IDtoName = new Config(this, "tile_owner_list"); + rafFile = new RandomAccessFile("shortmap.owner", "r"); + log.printMessage(Log.INFO, "Loading Map Tile Ownership..."); + int OwnColumns = rafFile.readInt(); + int OwnRows = rafFile.readInt(); + if (OwnColumns != MapColumns || OwnRows != MapRows) { + log.printMessage(Log.ERROR, "Map ownership array size (" + OwnColumns + "/" + OwnRows + ") does not match map size (" + MapColumns + "/" + MapRows + ")"); + } + log.printMessage(Log.ALWAYS, OwnColumns + "/" + OwnRows); + intMapOwnerID = new int[OwnColumns][OwnRows]; + for (x = 0; x < OwnColumns; x++) { + for (y = 0; y < OwnRows; y++) { + intMapOwnerID[x][y] = rafFile.readInt(); + } + } + rafFile.close(); + }*/ + + // Load Merchants + try { + rafFile = new RandomAccessFile("merchants", "r"); + Merchant mrcStore; + log.printMessage(Log.INFO, "Loading Merchants..."); + strStore = rafFile.readLine(); + while (!(strStore == null || strStore.equals(""))) { + mrcStore = new Merchant(this); + mrcStore.x = Integer.parseInt(strStore); + mrcStore.y = Integer.parseInt(rafFile.readLine()); + log.printMessage(Log.VERBOSE, "Merchant(" + mrcStore.x + "," + mrcStore.y + ")"); + strStore = rafFile.readLine(); + while (strStore != null && !strStore.equals("") && !strStore.equalsIgnoreCase("end")) { + log.printMessage(Log.DEBUG, "\t" + strStore); + mrcStore.items.add(strStore); + strStore = rafFile.readLine(); + } + if (!map.inside(mrcStore.x, mrcStore.y)) { + log.printMessage(Log.VERBOSE, "Previous merchant is off of the map, ignoring"); + blnMerchantListChanged = true; + } else { + //vctMerchants.add(mrcStore); + addDuskObject(mrcStore); + } + strStore = rafFile.readLine(); + } + } catch (Exception e) { + log.printError("DuskEngine():While loading merchants", e); + } + rafFile.close(); + + // Load Mobs + LivingThing thnStore; + rafFile = new RandomAccessFile("mobs", "r"); + log.printMessage(Log.INFO, "Loading Mobs..."); + strStore = rafFile.readLine(); + while (strStore != null) { + if (strStore.equals("")) { + break; + } + if (strStore.equals("mob2.3")) { + strStore = rafFile.readLine(); + log.printMessage(Log.VERBOSE, strStore); + try { + thnStore = new Mob(strStore, + Integer.parseInt(rafFile.readLine()), + Integer.parseInt(rafFile.readLine()), + this); + if (!map.inside(thnStore.x, thnStore.y)) { + log.printMessage(Log.VERBOSE, "Previous mob is off of the map, ignoring"); + blnMobListChanged = true; + } else { + //vctMobs.addElement(thnStore); + addDuskObject(thnStore); + } + } catch (Exception e) { + log.printError("DuskEngine():While loading mobs", e); + } + } else { + log.printMessage(Log.VERBOSE, strStore); + try { + thnStore = new Mob(strStore, + Integer.parseInt(rafFile.readLine()), + Integer.parseInt(rafFile.readLine()), + Integer.parseInt(rafFile.readLine()), + this); + if (!map.inside(thnStore.x, thnStore.y)) { + log.printMessage(Log.VERBOSE, "Previous mob is off of the map, ignoring"); + blnMobListChanged = true; + } else { + //vctMobs.addElement(thnStore); + addDuskObject(thnStore); + } + } catch (Exception e) { + log.printError("DuskEngine():While loading mobs", e); + } + } + strStore = rafFile.readLine(); + } + rafFile.close(); + + // Load signs + Sign sgnStore; + rafFile = new RandomAccessFile("signs", "r"); + log.printMessage(Log.INFO, "Loading Signs..."); + strStore = rafFile.readLine(); + while (!(strStore == null || strStore.equals(""))) { + log.printMessage(Log.VERBOSE, strStore); + sgnStore = new Sign(this, "sign", strStore, Integer.parseInt(rafFile.readLine()), Integer.parseInt(rafFile.readLine()), getID()); + if (!map.inside(sgnStore.x, sgnStore.y)) { + log.printMessage(Log.VERBOSE, "Previous sign is off of the map, ignoring"); + blnSignListChanged = true; + } else { + //vctSigns.add(sgnStore); + addDuskObject(sgnStore); + } + strStore = rafFile.readLine(); + } + rafFile.close(); + + // Load props + Prop prpStore; + rafFile = new RandomAccessFile("props", "r"); + log.printMessage(Log.INFO, "Loading Props..."); + strStore = rafFile.readLine(); + while (!(strStore == null || strStore.equals(""))) { + log.printMessage(Log.VERBOSE, strStore); + prpStore = getProp(strStore); + if (prpStore != null) { + prpStore.x = Integer.parseInt(rafFile.readLine()); + prpStore.y = Integer.parseInt(rafFile.readLine()); + if (!map.inside(prpStore.x, prpStore.y)) { + log.printMessage(Log.VERBOSE, "Previous prop is off of the map, ignoring"); + blnPropListChanged = true; + } else { + //vctProps.addElement(prpStore); + addDuskObject(prpStore); + } + } + strStore = rafFile.readLine(); + } + rafFile.close(); + + // Load global variables + String strVarName; + int intType; + String strObject; + double dblObject; + rafFile = new RandomAccessFile("globals", "r"); + log.printMessage(Log.INFO, "Loading Global Variables..."); + strVarName = rafFile.readLine(); + while (!(strVarName == null || strVarName.equals(""))) { + intType = Integer.parseInt(rafFile.readLine()); + switch (intType) { + case 0: { + dblObject = (double) Double.parseDouble(rafFile.readLine()); + varVariables.addVariable(strVarName, dblObject); + log.printMessage(Log.VERBOSE, strVarName + " = " + dblObject); + break; + } + case 1: { + strObject = rafFile.readLine(); + varVariables.addVariable(strVarName, strObject); + log.printMessage(Log.VERBOSE, strVarName + " = '" + strObject + "'"); + break; + } + } + strVarName = rafFile.readLine(); + } + rafFile.close(); + + //Start onBoot script + try { + Script scrOnBoot = new Script("conf/onBoot", this, true); + scrOnBoot.runScript(); + scrOnBoot.close(); + log.printMessage(Log.INFO, "Ran onBoot script."); + } catch (Exception e) { + log.printError("DuskEngine():Failed to run onBoot script, maybe it doesn't exist? (Not a fatal error)", e); + log.printMessage(Log.ALWAYS, "To create an onBoot script, type \"view conf onBoot\" in game as a high or master god."); + } + } catch (Exception e) { + log.printError("DuskEngine()", e); + } + } + + public boolean isGoodIP(String strIP) { + RandomAccessFile rafFile = null; + try { + // Why isn't this cached? + String strBlockedIP; + String key = strIP.toLowerCase(); + rafFile = new RandomAccessFile("conf/blockedIP", "r"); + strBlockedIP = rafFile.readLine(); + while (strBlockedIP != null) { + if (key.indexOf(strBlockedIP) != -1) { + rafFile.close(); + return false; + } + strBlockedIP = rafFile.readLine(); + } + rafFile.close(); + + return !isBanned(key); + } catch (Exception e) { + log.printError("isGoodIP():Checking for bad IP address", e); + return false; + } + } + + public boolean isGoodName(String strName) { + if (strName == null) { + return false; + } + if (strName.equals("") + || strName.length() > namecap + || strName.toLowerCase().equals("god") + || strName.toLowerCase().equals("default")) { + return false; + } + + String strValid = "0123456789][_'#"; + char[] letters = strName.toCharArray(); + char[] validChars = strValid.toCharArray(); + boolean blnValid = true; + + for (int n = 0; n < letters.length; n++) { + if (!Character.isLetter(letters[n])) { + blnValid = false; + for (int i = 0; i < validChars.length; i++) { + if (letters[n] == validChars[i]) { + blnValid = true; + } + } + } + } + if (!blnValid) { + return false; + } + + RandomAccessFile rafFile = null; + try { + String strDirtyWord; + String strLowerCaseName = strName.toLowerCase(); + rafFile = new RandomAccessFile("conf/dirtyWordFile", "r"); + strDirtyWord = rafFile.readLine(); + while (strDirtyWord != null) { + if (strLowerCaseName.indexOf(strDirtyWord) != -1) { + rafFile.close(); + return false; + } + strDirtyWord = rafFile.readLine(); + } + rafFile.close(); + } catch (Exception e) { + log.printError("isGoodName():" + strName + " had an error checking for bad name", e); + return false; + } + return true; + } + + synchronized void loadPrefs() { + Config settings = new Config(this, "conf/prefs"); + //Load Prefs + strLogFile = settings.getString("LogFileName", strLogFile); + if (strLogFile != null) { + try { + log = new Log(new PrintStream(new FileOutputStream(strLogFile, true), true)); + } catch (Exception e) { + log = new Log(System.out); + log.printError("loadPrefs():Opening log file \"" + strLogFile + "\"", e); + } + } else { + log = new Log(System.out); + } + log.printMessage(Log.INFO, "Loading Preferences..."); + + logLevel = settings.getInt("LoggingLevel", logLevel); + log.setLogLevel(logLevel); + + port = settings.getInt("Port", port); + tracker = settings.getBoolean("Tracker", tracker); + trackerport = settings.getInt("TrackerPort", trackerport); + trackername = settings.getString("TrackerName", trackername); + site = settings.getString("TrackerSite", site); + strRCAddress = settings.getString("RCAddress", strRCAddress); + strRCName = settings.getString("RCName", strRCName); + maxconnections = settings.getInt("MaxConnections", maxconnections); + blnMusic = settings.getBoolean("Music", blnMusic); + blnAI = settings.getBoolean("Ai", blnAI); + blnLineOfSight = settings.getBoolean("LineOfSight", blnLineOfSight); + blnIPF = settings.getBoolean("UniqueIPFilter", blnIPF); + changeRaceCpLimit = settings.getInt("ChangeRaceCPLimit", changeRaceCpLimit); + petcost = settings.getInt("PetCost", petcost); + messagecap = settings.getInt("MessageCap", messagecap); + namecap = settings.getInt("NameCap", namecap); + titlecap = settings.getInt("TitleCap", titlecap); + noChannelMax = settings.getInt("NoChannelMax", noChannelMax); + merchantimage = settings.getInt("MerchantImage", merchantimage); + signimage = settings.getInt("SignImage", signimage); + battlesound = settings.getInt("BattleSound", battlesound); + mobrespawnspeed = -1 * settings.getInt("MobReSpawnSpeed", mobrespawnspeed); + traincost = settings.getInt("TrainCost", traincost); + expgainmod = settings.getInt("ExpGainMod", expgainmod); + gplosemod = settings.getDouble("GpLoseMod", gplosemod); + explosemod = settings.getDouble("ExpLoseMod", explosemod); + gpfleemod = settings.getDouble("GpFleeMod", gpfleemod); + expfleemod = settings.getDouble("ExpFleeMod", expfleemod); + viewrange = settings.getInt("ViewRange", viewrange); + lngMobTicks = settings.getLong("MobTicks", lngMobTicks); + lngPlayerTicks = settings.getLong("PlayerTicks", lngPlayerTicks); + if (viewrange != oldviewrange) { + oldviewrange = viewrange; + mapsize = 1 + (2 * viewrange); + for (LivingThing thnStore : playersByName.values()) { + thnStore.resizeMap(); + } + } + //Load Triggered Scripts + + // FIXME: all this 'synchronized' stuff is busted to shit, you can't lock on something + // that might be null or will be replaced by a new object. + + try { + if (scrCanSeeLivingThing != null) { + synchronized (scrCanSeeLivingThing) { + scrCanSeeLivingThing.close(); + scrCanSeeLivingThing = new Script("conf/canSeeLivingThing", this, true); + } + } else { + scrCanSeeLivingThing = new Script("conf/canSeeLivingThing", this, true); + } + } catch (Exception e) { + scrCanSeeLivingThing = null; + } + try { + if (scrCanMoveThroughLivingThing != null) { + synchronized (scrCanMoveThroughLivingThing) { + scrCanMoveThroughLivingThing.close(); + scrCanMoveThroughLivingThing = new Script("conf/canMoveThroughLivingThing", this, true); + } + } else { + scrCanMoveThroughLivingThing = new Script("conf/canMoveThroughLivingThing", this, true); + } + } catch (Exception e) { + scrCanMoveThroughLivingThing = null; + } + try { + if (scrCanAttack != null) { + synchronized (scrCanAttack) { + scrCanAttack.close(); + scrCanAttack = new Script("conf/canAttack", this, true); + } + } else { + scrCanAttack = new Script("conf/canAttack", this, true); + } + } catch (Exception e) { + scrCanAttack = null; + } + try { + if (scrOnStart != null) { + synchronized (scrOnStart) { + scrOnStart.close(); + scrOnStart = new Script("conf/onStart", this, true); + } + } else { + scrOnStart = new Script("conf/onStart", this, true); + } + } catch (Exception e) { + scrOnStart = null; + } + try { + if (scrOnDeath != null) { + synchronized (scrOnDeath) { + scrOnDeath.close(); + scrOnDeath = new Script("conf/onDeath", this, true); + } + } else { + scrOnDeath = new Script("conf/onDeath", this, true); + } + } catch (Exception e) { + scrOnDeath = null; + } + try { + if (scrOnLogOut != null) { + synchronized (scrOnLogOut) { + scrOnLogOut.close(); + scrOnLogOut = new Script("conf/onLogOut", this, true); + } + } else { + scrOnLogOut = new Script("conf/onLogOut", this, true); + } + } catch (Exception e) { + scrOnLogOut = null; + } + + File f = new File("tileScriptMap"); + if (f.exists()) { + // Load tile scripts throuhg indirection + try { + Properties props = new Properties(); + try (FileInputStream fis = new FileInputStream(f)) { + props.load(fis); + + for (Entry e : props.entrySet()) { + String key = (String) e.getKey(); + String val = (String) e.getValue(); + + HashMap target = null; + String path = null; + String id = null; + if (key.startsWith("move.")) { + target = tileCanMove; + path = "defTileScripts/"; + id = key.substring(5); + } else if (key.startsWith("see.")) { + target = tileCanSee; + path = "defTileActions/"; + id = key.substring(4); + } else if (key.startsWith("action.")) { + target = tileAction; + path = "defTileSeeScripts/"; + id = key.substring(7); + } + if (target != null) { + try { + target.put(Integer.valueOf(id), new Script(path + val, this, true)); + } catch (Exception x) { + } + } + } + } + } catch (IOException x) { + } + } else { + // Load Tile Scripts + loadTileScripts(tileCanMove, "defTileScripts/"); + loadTileScripts(tileAction, "defTileActions/"); + loadTileScripts(tileCanSee, "defTileSeeScripts/"); + } + } + + void loadTileScripts(HashMap scripts, String path) { + try { + int i; + for (Script s : scripts.values()) { + s.close(); + } + scripts.clear(); + // FIXME: just try to load known tile sizes, we know how many tiles we have + for (i = 0; true; i++) { + try { + scripts.put(i, new Script(path + i, this, true)); + } catch (Exception e) { + break; + } + } + } catch (Exception e) { + } + } + + public synchronized long getID() { + nextid++; + return nextid; + } + + public void chatMessage(String msg, String from) { + from = from.toLowerCase(); + log.printMessage(Log.ALWAYS, msg); + for (LivingThing lt : playersByName.values()) { + if (!lt.ignoreList.contains(from)) { + lt.chatMessage(msg); + } + } + } + + public void chatMessage(String inMessage, int locx, int locy, String strFrom) { + strFrom = strFrom.toLowerCase(); + LivingThing thnStore; + log.printMessage(Log.ALWAYS, inMessage); + + for (TileMap.MapData md : map.range(locx, locy, viewrange)) { + for (DuskObject o : md.entities) { + if (o.isLivingThing()) { + thnStore = (LivingThing) o; + if (thnStore.isPlayer() + && !thnStore.ignoreList.contains(strFrom)) { + thnStore.chatMessage(inMessage); + } + } + } + } + } + + public void chatMessage(String msg, String clan, String from) { + from = from.toLowerCase(); + log.printMessage(Log.ALWAYS, msg); + for (LivingThing lt : playersByName.values()) { + if (lt.clan.equals(clan) + && !lt.ignoreList.contains(from)) { + lt.chatMessage(msg); + } + } + } + + public void refreshEntities(LivingThing refresh) { + LinkedList newEntities = new LinkedList<>(); + + for (TileMap.MapData md : map.range(refresh.x, refresh.y, viewrange)) { + if (!md.entities.isEmpty() + && canSeeTo(refresh, md.x, md.y)) { + for (DuskObject o : md.entities) { + newEntities.add(o); + //old = thnRefresh.removeEntity(objStore.ID); + //for (i4 = 0; i4 < thnRefresh.nearEntities.size(); i4++) { + // objStore2 = (DuskObject) thnRefresh.nearEntities.elementAt(i4); + // if (objStore2 == objStore) { + // thnRefresh.nearEntities.removeElementAt(i4); + // i4 = -1; + // break; + // } + //} + if (o.isPlayerMerchant()) { + PlayerMerchant shop = (PlayerMerchant) o; + if (refresh.x == shop.x && refresh.y == shop.y) { + //strResult = (char) 17 + ""; + StringBuilder sb = new StringBuilder(); + for (String name : shop.vctItems.keySet()) { + LinkedList vctStore = shop.vctItems.get(name); + Item item = (Item) vctStore.element(); + int intCost = (item.intCost * 3) / 4; + + if (refresh.name.equalsIgnoreCase(shop.strOwner)) { + intCost = 0; + } + sb.append(intCost + "gp)" + name + "\n"); + } + sb.append(".\n"); + refresh.send(MessageType.UpdateMerchant, sb.toString()); + refresh.updateSell(); + } + } + if (o.isMerchant()) { + Merchant merchant = (Merchant) o; + if (refresh.x == merchant.x && refresh.y == merchant.y) { + if (true) { + StringBuilder sb = new StringBuilder(); + //strResult = (char) 17 + ""; + if (refresh.getFollowing() != null && refresh.getFollowing().isPet()) { + for (int i5 = 0; i5 < merchant.items.size(); i5++) { + String itemname = (String) merchant.items.get(i5); + Item item = getItem(itemname); + if (item != null) { + sb.append(item.intCost).append("gp)").append(itemname).append("\n"); + } else if (itemname.equals("pet")) { + sb.append(petcost).append("gp)").append(itemname).append("\n"); + } else { + sb.append(traincost).append("exp)").append(itemname).append("\n"); + sb.append(traincost).append("exp)").append(refresh.getFollowing().name).append(":").append(itemname).append("\n"); + } + } + } else { + for (int i5 = 0; i5 < merchant.items.size(); i5++) { + String itemname = (String) merchant.items.get(i5); + Item item = getItem(itemname); + if (item != null) { + sb.append(item.intCost).append("gp)").append(itemname).append("\n"); + } else if (itemname.equals("pet")) { + sb.append(petcost).append("gp)").append(itemname).append("\n"); + } else { + sb.append(traincost).append("exp)").append(itemname).append("\n"); + } + } + } + sb.append(".\n"); + refresh.send(MessageType.UpdateMerchant, sb.toString()); + refresh.updateSell(); + } else { + HashMap items = new HashMap<>(); + for (String itemname : merchant.items) { + TransactionItem titem = items.get(itemname); + if (titem == null) { + Item item = getItem(itemname); + if (item != null) { + titem = new TransactionItem(itemname, 1, item.intCost, "gp"); + } else if (itemname.equals("pet")) { + titem = new TransactionItem(itemname, 1, petcost, "gp"); + } else { + if (refresh.getFollowing() != null && refresh.getFollowing().isPet()) { + titem = new TransactionItem(refresh.getFollowing().name + ":" + itemname, 1, traincost, "exp"); + items.put(titem.name, titem); + } + titem = new TransactionItem(itemname, 1, traincost, "exp"); + //sb.append(traincost).append("exp)").append(itemname).append("\n"); + //sb.append(traincost).append("exp)").append(refresh.getFollowing().name).append(":").append(itemname).append("\n"); + } + items.put(titem.name, titem); + } else { + titem.count++; + } + } + refresh.send(new ServerMessage.ItemsMessage(MessageType.UpdateMerchant, new ArrayList<>(items.values()))); + refresh.updateSell(); + } + } + //if (old == null) { //i4 != -1 + // if (!objStore.isLivingThing() + // || canSeeLivingThing(thnRefresh, (LivingThing) objStore)) { + // thnRefresh.send(MessageType.AddEntity, objStore.toEntity()); + // } + //} + } + } + } + } + // Remove those left + //for (DuskObject o: thnRefresh.nearEntities) { + // thnRefresh.send(MessageType.RemoveEntity, o.ID + "\n"); + //} + //thnRefresh.nearEntities = vctNewEntities; + refresh.setEntities(newEntities); + } + + public void addEntity(DuskObject add) { + for (TileMap.MapData md : map.range(add.x, add.y, viewrange)) { + for (DuskObject o : md.entities) { + if (o.isLivingThing()) { + LivingThing lt = (LivingThing) o; + if (lt.isPlayer()) { + // FIXME: see if this can be merged with updateEntity() above + + if (canSeeTo(lt, add.x, add.y)) { + if ((!add.isLivingThing() + || canSeeLivingThing(lt, (LivingThing) add))) { + lt.addEntity(add); + } + } + } + } + } + } + } + + public void cleanup() { + log.printMessage(Log.INFO, "Starting cleanup."); + for (TileMap.MapData md : map) { + for (DuskObject o : md.entities) { + if (o.isLivingThing()) { + LivingThing lt = (LivingThing) o; + if (lt.isPlayer()) { + if (!playersByName.containsKey(lt.name)) { + log.printMessage(Log.INFO, "**found defunct player at " + md.x + "," + md.y + " during cleanup."); + removeDuskObject(lt); + lt.battle = null; + lt.isStopped = true; + } + } + if (lt.isPet()) { + if (!petList.contains(lt)) { + log.printMessage(Log.INFO, "**found defunct pet at " + md.x + "," + md.y + " during cleanup."); + removeDuskObject(lt); + } + } + } + } + } + log.printMessage(Log.INFO, "Finished cleanup."); + } + + void notifyRemoved(DuskObject remove) { + for (TileMap.MapData md : map.range(remove.x, remove.y, viewrange)) { + for (DuskObject o : md.entities) { + if (o.isLivingThing()) { + LivingThing lt = (LivingThing) o; + + if (lt.isPlayer()) { + if (canSeeTo(lt, remove.x, remove.y)) { + lt.removeEntity(remove.ID); + } + } + } + } + } + } + + public void newBattle(LivingThing pla1, LivingThing pla2) { + if (pla2 == null) { + return; + } + if (pla1.isPet()) { + pla1.chatMessage("Pets cannot lead battles."); + return; + } + if (pla1.battle != null) { + pla1.chatMessage("You're already fighting!"); + return; + } + if (pla1 == pla2) { + pla1.chatMessage("You can't fight yourself!"); + return; + } + if (pla2.isPet()) { + pla1.chatMessage("You can't attack pets."); + return; + } +// if (Math.abs(pla1.intLocX - pla2.intLocX) + Math.abs(pla1.intLocY - pla2.intLocY) > 1) + if (Math.abs(pla1.x - pla2.x) + Math.abs(pla1.y - pla2.y) > pla1.getRangeWithBonus()) { + System.out.println("mob range = " + Math.abs(pla1.x - pla2.x) + Math.abs(pla1.y - pla2.y)); + System.out.println("player range = " + pla1.getRangeWithBonus()); + pla1.chatMessage("They're too far away."); + return; + } + if (scrCanAttack != null) { + synchronized (scrCanAttack) { + scrCanAttack.varVariables.clearVariables(); + scrCanAttack.varVariables.addVariable("attacking", pla1); + scrCanAttack.varVariables.addVariable("attacked", pla2); + if (!scrCanAttack.rewindAndParseScript()) { + pla1.chatMessage("You can't attack them."); + return; + } + } + } + // Check if the attacked is following the attacker + LivingThing thnStore = pla2; + while (thnStore != null) { + if (pla1 == thnStore) { + pla1.chatMessage("You can't attack a member of your group."); + return; + } + if (thnStore.isPlayer() && !pla1.isMob()) { + if (thnStore.clan == null || thnStore.clan.equalsIgnoreCase("none")) { + pla1.chatMessage("You can't fight them."); + return; + } + } + thnStore = thnStore.getMaster(); + } + // Check if the attacker is following the attacked + thnStore = pla2; + while (thnStore != null) { + if (pla1 == thnStore) { + pla1.chatMessage("You can't attack a member of your group."); + return; + } + if (thnStore.isPlayer() && !pla1.isMob()) { + if (thnStore.clan == null || thnStore.clan.equalsIgnoreCase("none")) { + pla1.chatMessage("You can't fight them."); + return; + } + } + thnStore = thnStore.getFollowing(); + } + if (pla2.battle == null) { + if ((pla1.isPlayer() && pla2.isPlayer()) && (pla1.clan.equals("none") || pla2.clan.equals("none"))) { + pla1.chatMessage("Players who are not in clans cannot fight other players."); + return; + } + if (pla2.isPlayer() && overMerchant(pla2.x, pla2.y) != null) { + pla1.chatMessage("You cannot attack players who are shopping."); + return; + } + if (pla2.isPlayer() && overPlayerMerchant(pla2.x, pla2.y) != null) { + pla1.chatMessage("You cannot attack players who are shopping."); + return; + } + // FIXME: locks? + battleList.add(new Battle(pla1, pla2, this)); + return; + } else { + if (pla2.battleSide == 1) { + thnStore = pla1; + while (thnStore != null) { + if (thnStore.battle == null) { + pla2.battle.addToBattle(thnStore, 2); + } + thnStore = thnStore.getFollowing(); + } + thnStore = pla1.getMaster(); + while (thnStore != null) { + if (thnStore.battle == null) { + pla2.battle.addToBattle(thnStore, 2); + } + thnStore = thnStore.getMaster(); + } + } else { + thnStore = pla1; + while (thnStore != null) { + if (thnStore.battle == null) { + pla2.battle.addToBattle(thnStore, 1); + } + thnStore = thnStore.getFollowing(); + } + thnStore = pla1.getMaster(); + while (thnStore != null) { + if (thnStore.battle == null) { + pla2.battle.addToBattle(thnStore, 1); + } + thnStore = thnStore.getMaster(); + } + } +// pla2.batBattle.chatMessage("\t"+pla1.strName+" has joined the battle."); + return; + } + } + + // Not used + @Deprecated + public Sign getSign(String inName) { + int i; + Sign sgnStore; + for (i = 0; i < signList.size(); i++) { + sgnStore = (Sign) signList.get(i); + if (inName.equals(sgnStore.name)) { + return sgnStore; + } + } + return null; + } + + public LivingThing getPet(String name) { + // FIXME: hash map? + for (LivingThing pet : petList) { + if (name.equalsIgnoreCase(pet.name)) { + return pet; + } + } + return null; + } + + public LivingThing getPlayer(String name) { + name = name.toLowerCase(); + + return playersByName.get(name); + } + + public Faction getFaction(String name) { + Faction faction; + + faction = factionMap.get(name); + if (faction == null) { + faction = new Faction(name, this); + factionMap.put(name, faction); + } + return faction; + } + + public LivingThing getMobFromVct(String inName) { + // FIXME: hash map? + synchronized (mobList) { + for (Mob mob : mobList) { + if (mob.name.equals(inName)) { + return mob; + } + } + } + return null; + } + + // FIXME: io on prop as per Item + Prop getProp(String strName) { + Prop prpStore = null; + try { + RandomAccessFile rafPropDef = new RandomAccessFile("defProps/" + strName, "r"); + prpStore = new Prop(getID(), strName); + String strStore = rafPropDef.readLine(); + while (!(strStore == null || strStore.equals("."))) { + if (strStore.equalsIgnoreCase("description")) { + prpStore.description = rafPropDef.readLine(); + } else if (strStore.equalsIgnoreCase("image")) { + prpStore.intImage = Integer.parseInt(rafPropDef.readLine()); + } + strStore = rafPropDef.readLine(); + } + rafPropDef.close(); + } catch (Exception e) { + log.printError("getProp():While trying to get prop \"" + strName + "\"", e); + } + return prpStore; + } + + // Unused + @Deprecated + Prop getPropFromVct(String inName) { + try { + int i = 0; + Prop prpStore; + while (true) { + prpStore = (Prop) propList.get(i); + if (prpStore.name.equals(inName)) { + return prpStore; + } + i++; + } + } catch (Exception e) { + log.printError("getPropFromVct():While trying to get prop \"" + inName + "\"", e); + } + return null; + } + + // Unused + @Deprecated + Prop getPropFromVctAndRemove(String inName) { + try { + int i = 0; + Prop prpStore; + while (true) { + prpStore = (Prop) propList.get(i); + if (prpStore.name.equals(inName)) { + //vctProps.remove(i); + removeDuskObject(prpStore); + return prpStore; + } + i++; + } + } catch (Exception e) { + log.printError("getPropFromVctAndRemove():While trying to get prop \"" + inName + "\"", e); + } + return null; + } + + @Deprecated + public Condition getCondition(String inName) { + try { + return Condition.getCondition(inName); + } catch (Exception e) { + log.printError("getCondition():Parsing condition \"" + inName + "\"", e); + } + return null; + } + + int getSpellPercent(String strName) { + try { + RandomAccessFile rafSpell = new RandomAccessFile("defSpells/" + strName, "r"); + SpellGroup grpStore = getSpellGroup(rafSpell.readLine()); + rafSpell.close(); + return grpStore.getSpellPercent(strName); + } catch (Exception e) { + log.printError("getSpellPercent():While trying to get info on spell \"" + strName + "\"", e); + } + return -1; + } + + // FIXME: do i/o in SpellGroup + @Deprecated + public SpellGroup getSpellGroup(String strName) { + SpellGroup group; + String key = strName.toLowerCase(); + + group = spellGroupList.get(key); + if (group == null) { + group = new SpellGroup(strName); + try (RandomAccessFile rafSpellGroup = new RandomAccessFile("defSpellGroups/" + strName, "r")) { + String strStore = rafSpellGroup.readLine(); + while (strStore != null && !strStore.equals("") && !strStore.equals(".")) { + group.vctSpells.add(strStore); + strStore = rafSpellGroup.readLine(); + } + spellGroupList.put(key, group); + } catch (IOException e) { + log.printError("trying to read spell group file for \"" + strName + "\"", e); + group = null; + } + } + return group; + } + + // FIXME: move this to Item + @Deprecated + public Item getItem(String inName) { + try { + return Item.getItem(getID(), inName); + } catch (IOException ex) { + log.printError("getItem():Parsing item \"" + inName + "\"", ex); + return null; + } + } + + // Appears unused + @Deprecated + Item getItemFromVct(String inName) { + try { + int i; + Item itmStore; + StringTokenizer tokName = new StringTokenizer(inName, " "); + String strStore = tokName.nextToken(); + i = Integer.parseInt(strStore); + strStore = inName.substring(strStore.length() + 1, inName.length()); + itmStore = (Item) itemList.get(i); + if (itmStore.name.equals(strStore)) { + return itmStore; + } + } catch (Exception e) { + log.printError("getItemFromVct():For item \"" + inName + "\"", e); + } + return null; + } + + // Appears unused + @Deprecated + Item getItemFromVctAndRemove(String inName) { + try { + int i; + Item itmStore; + StringTokenizer tokName = new StringTokenizer(inName, " "); + String strStore = tokName.nextToken(); + i = Integer.parseInt(strStore); + strStore = inName.substring(strStore.length() + 1, inName.length()); + itmStore = (Item) itemList.get(i); + if (itmStore.name.equals(strStore)) { + //vctItems.remove(i); + removeDuskObject(itmStore); + return itmStore; + } + } catch (Exception e) { + log.printError("getItemFromVctAndRemove():For item \"" + inName + "\"", e); + } + return null; + } + + void playSound(int sfxid, int locx, int locy) { + for (TileMap.MapData md : map.range(locx, locy, viewrange)) { + for (DuskObject o : md.entities) { + if (o.isLivingThing()) { + LivingThing lt = (LivingThing) o; + if (lt.isPlayer()) { + lt.playSFX(sfxid); + } + } + } + } + } + + public boolean canMoveTo(int inLocX, int inLocY, LivingThing thnStore) { + int i; + LivingThing thnStore2; + Script scrStore; + boolean blnStore; + + if (!map.inside(inLocX, inLocY)) + return false; + + for (DuskObject o : map.getEntities(inLocX, inLocY, null)) { + if (o.isLivingThing()) { + thnStore2 = (LivingThing) o; + if (!canMoveThrougLivingThing(thnStore, thnStore2)) + return false; + } + } + try { + scrStore = new Script("defCanMoveScripts/" + inLocX + "_" + inLocY, this, false); + scrStore.varVariables.addVariable("trigger", thnStore); + blnStore = scrStore.rewindAndParseScript(); + scrStore.close(); + return blnStore; + } catch (Exception e) { + } + try { + scrStore = (Script) tileCanMove.get((int) map.getTile(inLocX, inLocY)); + synchronized (scrStore) { + scrStore.varVariables.clearVariables(); + scrStore.varVariables.addVariable("trigger", thnStore); + blnStore = scrStore.rewindAndParseScript(); + } + return blnStore; + } catch (Exception e) { + //e.printStackTrace(); + } + return false; + } + + boolean canSee(int inLocX, int inLocY, LivingThing thnStore) { + int i; + LivingThing thnStore2; + DuskObject objStore = null; + Script scrStore; + boolean blnStore; + try { + scrStore = new Script("defCanSeeScripts/" + inLocX + "_" + inLocY, this, false); + scrStore.varVariables.addVariable("trigger", thnStore); + blnStore = scrStore.rewindAndParseScript(); + scrStore.close(); + return blnStore; + } catch (Exception e) { + } + try { + scrStore = (Script) tileCanSee.get((int) map.getTile(inLocX, inLocY)); + synchronized (scrStore) { + scrStore.varVariables.clearVariables(); + scrStore.varVariables.addVariable("trigger", thnStore); + blnStore = scrStore.rewindAndParseScript(); + } + return blnStore; + } catch (Exception e) { + } + return false; + } + + // TODO: Move this to map, then everything that uses it can be elsewhere + // Following by Randall Leeds and Tom Weingarten + // map/iterator version by notzed + public boolean canSeeTo(LivingThing thing, int destX, int destY) { + if (Math.abs(thing.x - destX) > viewrange || Math.abs(thing.y - destY) > viewrange) { + return false; + } + + if (!blnLineOfSight) { + return true; + } + + for (TileMap.MapData md : map.look(thing.x, thing.y, destX, destY)) { + if (!canSee(md.x, md.y, thing)) + return false; + } + return true; + /* + int tempX = thing.x; + int tempY = thing.y; + + // TODO: put looking stuff on map + // TODO: put path finding on map + // FIXME: this exception stuff is just used for bounds checking ... not good + // FIXME: logic could be improved using directions + try { + while (!(Math.abs(tempX - destX) < 2 && Math.abs(tempY - destY) < 2)) { + if (Math.abs(tempX - destX) > Math.abs(tempY - destY)) { + if (tempX > destX) { + if (!(canSee(tempX - 1, tempY, thing))) { + return false; + } + tempX--; + } else { + if (!(canSee(tempX + 1, tempY, thing))) { + return false; + } + tempX++; + } + } else if (Math.abs(tempX - destX) < Math.abs(tempY - destY)) { + if (tempY > destY) { + if (!(canSee(tempX, tempY - 1, thing))) { + return false; + } + tempY--; + } else { + if (!(canSee(tempX, tempY + 1, thing))) { + return false; + } + tempY++; + } + } else { + if (tempX > destX && tempY > destY) { + if (!(canSee(tempX - 1, tempY - 1, thing))) { + return false; + } + tempX--; + tempY--; + } else if (tempX < destX && tempY < destY) { + if (!(canSee(tempX + 1, tempY + 1, thing))) { + return false; + } + tempX++; + tempY++; + } else if (tempX > destX && tempY < destY) { + if (!(canSee(tempX - 1, tempY + 1, thing))) { + return false; + } + tempX--; + tempY++; + } else { + if (!(canSee(tempX + 1, tempY - 1, thing))) { + return false; + } + tempX++; + tempY--; + } + } + } + } catch (Exception e) { + return false; + } + + return true; + * */ + } + //End contributed portion + + public Merchant overMerchant(int x, int y) { + for (DuskObject o : map.getEntities(x, y, null)) { + if (o.isMerchant()) { + return (Merchant) o; + } + } + return null; + } + + public PlayerMerchant overPlayerMerchant(int x, int y) { + for (DuskObject o : map.getEntities(x, y, null)) { + if (o.isPlayerMerchant()) { + return (PlayerMerchant) o; + } + } + return null; + } + + synchronized void changeMap(int locx, int locy, short value) { + if (value < 0 || value > tileCanMove.size()) { + log.printMessage(Log.INFO, "Invalid value passed to changeMap(" + locx + "," + locy + "," + value + ")"); + return; + } + if (!map.inside(locx, locy)) { + log.printMessage(Log.INFO, "Invalid location to changeMap(" + locx + "," + locy + "," + value + ")"); + return; + } + map.setTile(locx, locy, value); + blnMapHasChanged = true; + updateMap(locx, locy); + } + + synchronized void resizeMap(int x, int y) { + map.resize(x, y); + for (LivingThing lt : playersByName.values()) { + lt.resizeMap(); + } + blnMapHasChanged = true; + } + + void saveMap() { + if (blnSavingGame) { + return; + } + try { + blnSavingGame = true; + StringTokenizer tknStore; + Mob mobStore; + Item itmStore; + Sign sgnStore; + Merchant mrcStore; + Prop prpStore; + File deleteme; + RandomAccessFile rafFile; + int i, + i2; + if (blnMapHasChanged) { + log.printMessage(Log.ALWAYS, "Saving map..."); + map.saveMap(new File("shortmap")); + String strMapLog = "shortmap_redraw"; + PrintStream psMap = new PrintStream(new FileOutputStream(strMapLog, true), true); + psMap.println("# Map Saved"); + psMap.close(); + blnMapHasChanged = false; + } + + if (blnMobListChanged) { + log.printMessage(Log.ALWAYS, "Saving mobs..."); + synchronized (mobList) { + deleteme = new File("mobs"); + deleteme.delete(); + rafFile = new RandomAccessFile("mobs", "rw"); + for (Mob mob : mobList) { + if (mob.blnOneUse == false) { + if (mob.level == -1) { + rafFile.writeBytes("mob2.3\n" + mob.name + "\n" + mob.originalX + "\n" + mob.originalY + "\n"); + } else { + rafFile.writeBytes(mob.name + "\n" + mob.level + "\n" + mob.originalX + "\n" + mob.originalY + "\n"); + } + } + } + rafFile.close(); + } + blnMobListChanged = false; + } + + if (blnSignListChanged) { + log.printMessage(Log.ALWAYS, "Saving signs..."); + synchronized (signList) { + deleteme = new File("signs"); + deleteme.delete(); + rafFile = new RandomAccessFile("signs", "rw"); + for (Sign sign : signList) { + rafFile.writeBytes(sign.strMessage + "\n" + sign.x + "\n" + sign.y + "\n"); + } + rafFile.close(); + } + blnSignListChanged = false; + } + + if (blnMerchantListChanged) { + log.printMessage(Log.ALWAYS, "Saving merchants..."); + synchronized (merchantList) { + deleteme = new File("merchants"); + deleteme.delete(); + try (RandomAccessFile out = new RandomAccessFile("merchants", "rw")) { + // FIXME: i/o on object + for (Merchant m : merchantList) { + out.writeBytes(m.x + "\n" + m.y + "\n"); + for (i2 = 0; i2 < m.items.size(); i2++) { + out.writeBytes((String) m.items.get(i2) + "\n"); + } + out.writeBytes("end\n"); + } + } + } + blnMerchantListChanged = false; + } + + if (blnPropListChanged) { + log.printMessage(Log.ALWAYS, "Saving props..."); + synchronized (propList) { + deleteme = new File("props"); + deleteme.delete(); + rafFile = new RandomAccessFile("props", "rw"); + for (Prop prop : propList) { + rafFile.writeBytes(prop.name + "\n" + prop.x + "\n" + prop.y + "\n"); + } + rafFile.close(); + } + blnPropListChanged = false; + } + + if (blnVariableListChanged) { + log.printMessage(Log.ALWAYS, "Saving global variables..."); + synchronized (varVariables) { + deleteme = new File("globals"); + deleteme.delete(); + rafFile = new RandomAccessFile("globals", "rw"); + for (Variable varStore : varVariables.vctVariables.values()) { + if (varStore.isString() || varStore.isNumber()) { + rafFile.writeBytes(varStore.strName + "\n"); + rafFile.writeBytes(varStore.bytType + "\n"); + rafFile.writeBytes(varStore.objData + "\n"); + } + } + rafFile.close(); + } + blnVariableListChanged = false; + } + log.printMessage(Log.ALWAYS, "Saved game settings without error"); + } catch (Exception e) { + log.printError("saveMap():While saving game settings", e); + } + blnSavingGame = false; + } + + void backupMap() { + try { + StringTokenizer tknStore; + Mob mobStore; + Item itmStore; + Sign sgnStore; + Merchant mrcStore; + Prop prpStore; + synchronized (map) { + File deleteme; + RandomAccessFile rafFile; + int i, i2; + + map.saveMap(new File("backup/shortmap.backup")); + + deleteme = new File("backup/mobs.backup"); + deleteme.delete(); + rafFile = new RandomAccessFile("backup/mobs.backup", "rw"); + synchronized (mobList) { + for (Mob mob : mobList) { + if (mob.blnOneUse == false) { + tknStore = new StringTokenizer(mob.name, " "); + rafFile.writeBytes(tknStore.nextToken() + "\n" + mob.level + "\n" + mob.originalX + "\n" + mob.originalY + "\n"); + } + } + } + rafFile.close(); + + deleteme = new File("backup/signs.backup"); + deleteme.delete(); + rafFile = new RandomAccessFile("backup/signs.backup", "rw"); + for (Sign sign : signList) { + rafFile.writeBytes(sign.strMessage + "\n" + sign.x + "\n" + sign.y + "\n"); + } + rafFile.close(); + + deleteme = new File("backup/merchants.backup"); + deleteme.delete(); + rafFile = new RandomAccessFile("backup/merchants.backup", "rw"); + for (Merchant m : merchantList) { + rafFile.writeBytes(m.x + "\n" + m.y + "\n"); + for (i2 = 0; i2 < m.items.size(); i2++) { + rafFile.writeBytes((String) m.items.get(i2) + "\n"); + } + rafFile.writeBytes("end\n"); + } + rafFile.close(); + + deleteme = new File("backup/props.backup"); + deleteme.delete(); + rafFile = new RandomAccessFile("backup/props.backup", "rw"); + for (Prop prop : propList) { + rafFile.writeBytes(prop.name + "\n" + prop.x + "\n" + prop.y + "\n"); + } + rafFile.close(); + } + log.printMessage(Log.ALWAYS, "Backed up game settings without error"); + } catch (Exception e) { + log.printError("backupMap():While backing up settings", e); + } + } + + @Deprecated + DuskObject getDuskObject(int x, int y, String name) { + //synchronized (entities) { + // return DuskObject.find(entities[x][y], name); + //} + for (DuskObject o : map.getEntities(x, y, null)) { + if (o.name.equalsIgnoreCase(name)) + return o; + + } + return null; + } + + // must have objEntities locked + @Deprecated + private void pushDuskObject(DuskObject o) { + map.addEntity(o); + } + + @Deprecated + private void popDuskObject(DuskObject o) { + map.removeEntity(o); + } + + public void addDuskObject(DuskObject obj) { + if (obj.isLivingThing()) { + LivingThing lt = (LivingThing) obj; + if (!lt.isLoaded) { + return; + } + if (obj instanceof Mob) { + synchronized (mobList) { + mobList.add((Mob) obj); + } + blnMobListChanged = true; + } else if (lt.isPet()) { + petList.add(lt); + } + } else if (obj.isItem()) { + itemList.add((Item) obj); + } else if (obj.isSign()) { + signList.add((Sign) obj); + blnSignListChanged = true; + } else if (obj.isMerchant()) { + merchantList.add((Merchant) obj); + blnMerchantListChanged = true; + } else if (obj.isProp()) { + propList.add((Prop) obj); + blnPropListChanged = true; + } + + + pushDuskObject(obj); + addEntity(obj); + } + + /** + * When a mob is killed, it isn't really removed, but the player needs + * to know about it. + * + * This 'cheats' by removing it from the game but temporarily + * corrupting the mob index. + * + * @param obj + */ + public void mobKilled(Mob obj) { + notifyRemoved(obj); + popDuskObject(obj); + } + + public void removeDuskObject(DuskObject obj) { + if (obj.isLivingThing()) { + LivingThing lt = (LivingThing) obj; + if (!lt.isLoaded) { + return; + } + if (lt.isMob()) { + synchronized (mobList) { + mobList.remove(obj); + } + blnMobListChanged = true; + } else if (lt.isPet()) { + petList.remove(obj); + } + } else if (obj.isItem()) { + itemList.remove(obj); + } else if (obj.isSign()) { + signList.remove(obj); + blnSignListChanged = true; + } else if (obj.isMerchant()) { + merchantList.remove(obj); + blnMerchantListChanged = true; + } else if (obj.isProp()) { + propList.remove(obj); + blnPropListChanged = true; + } + + notifyRemoved(obj); + popDuskObject(obj); + } + + public Iterable visibleMap(int x, int y) { + return map.range(x, y, viewrange); + } + + /** + * Move a living thing, updating any other living things within range + * + * @param thing + * @param inlocx + * @param inlocy + * @param dir + */ + // FIXME: now i think this probably needs to be moved back to livingthing ... + public void moveDuskObject(LivingThing thing, int inlocx, int inlocy, MessageType dir) { + for (TileMap.MapData md : map.range(thing.x, thing.y, viewrange)) { + for (DuskObject o : md.entities) { + if (o.isLivingThing()) { + LivingThing lt = (LivingThing) o; + if (lt.isPlayer()) { + boolean canSee = canSeeTo(lt, inlocx, inlocy) + && (!thing.isLivingThing() + || canSeeLivingThing(lt, thing)); + + if (canSee) { + // Add/update it if now visible + lt.addEntity(thing); + lt.send(dir, thing.ID + "\n"); + } else { + // Remove it if it isn't + lt.removeEntity(thing.ID); + } + } else if (lt.isMob()) { + // Is this true? + Mob thnMob = (Mob) lt; + thnMob.blnCanSeePlayer = true; + } + } + } + } + // Move it from cell to cell + popDuskObject(thing); + thing.x = inlocx; + thing.y = inlocy; + pushDuskObject(thing); + //addDuskObject(objIn); + } + + /** + * Find a visible object of the given name. + * + * Name may also be the id. + * + * @param thing + * @param name + * @return + */ + public DuskObject findVisibleObject(LivingThing thing, String name) { + int number = 0; + int byid = -1; + + // First see if this finding an object by unique id. + // Since .. the server and client have different ideas about "id numbers" (FFS) + try { + byid = Integer.valueOf(name); + number = 0; + } catch (NumberFormatException e) { + } + + if (byid == -1) { + int i = name.indexOf("."); + if (i != -1) { + try { + number = Integer.parseInt(name.substring(0, i)); + name = name.substring(i + 1, name.length()); + } catch (NumberFormatException e) { + number = 0; + } + } + } + //Search surrounding area + + for (TileMap.MapData md : map.range(thing.x, thing.y, viewrange)) { + for (DuskObject o : md.entities) { + if (byid == o.ID + || (byid == -1 && o.name.equalsIgnoreCase(name))) { + if ((!o.isLivingThing() + || canSeeLivingThing(thing, (LivingThing) o)) + && canSeeTo(thing, o.x, o.y)) { + if (number == 0) { + return o; + } else { + number--; + } + } + } + } + } + + return null; + } + + void updateMap(int locx, int locy) { + for (TileMap.MapData md : map.range(locx, locy, viewrange)) { + for (DuskObject o : md.entities) { + if (o.isLivingThing()) { + LivingThing lt = (LivingThing) o; + if (lt.isPlayer()) { + lt.updateMap(); + } + } + } + } + } + + public void run() { + log.printMessage(Log.ALWAYS, "Mob ticks = " + lngMobTicks); + log.printMessage(Log.ALWAYS, "Player ticks = " + lngPlayerTicks); + log.printMessage(Log.ALWAYS, "Starting Ticks"); + int tick = 0, + i; + LivingThing thnStore, + thnStore2; + Battle batStore; + long lngTime = System.currentTimeMillis(), + lngPause = 0; + while (true) { + try { + //250 milliseconds per tick + lngPause = lngPlayerTicks - (System.currentTimeMillis() - lngTime); + if (lngPause > 0) { + Thread.currentThread().sleep(lngPause); + } + lngTime = System.currentTimeMillis(); + if (tick % 73 == 0) { + for (LivingThing pet : petList) { + if (pet.battle == null) { + if (pet.isSleeping) { + pet.hp += 3 + (pet.cons + pet.consbon); + if (pet.hp > (pet.maxhp + pet.hpbon)) { + pet.hp = (pet.maxhp + pet.hpbon); + } + pet.mp += 3 + (pet.wisd + pet.wisdbon); + if (pet.mp > (pet.maxmp + pet.mpbon)) { + pet.mp = (pet.maxmp + pet.mpbon); + } + } else { + pet.hp += 1 + ((pet.cons + pet.consbon) / 2); + if (pet.hp > (pet.maxhp + pet.hpbon)) { + pet.hp = (pet.maxhp + pet.hpbon); + } + pet.mp += 1 + ((pet.wisd + pet.wisdbon) / 2); + if (pet.mp > (pet.maxmp + pet.mpbon)) { + pet.mp = (pet.maxmp + pet.mpbon); + } + } + } + if (pet.getMaster() != null) { + if (!pet.getMaster().isWorking) { + pet.close(); + continue; + } + boolean blnTmpShouldSave = pet.isSaveNeeded; + pet.getMaster().updateStats(); + pet.isSaveNeeded = blnTmpShouldSave; + } else { + pet.close(); + continue; + } + pet.savePlayer(); + } + } + for (LivingThing pet : petList) { + pet.moveTick(); + } + //Following code submitted by Randall Leeds, revised by Tom Weingarten: + for (LivingThing lt : playersByName.values()) { + if (!lt.isWorking) { + //thnStore.closeNoMsgPlayer(); + continue; + } + if (!lt.isSaveable) { + continue; + } + if (lt.noChannel > 0 && tick % 4 == 0) { + lt.noChannel--; + } + lt.moveTick(); + if (tick % 73 == 0) { + if (lt.battle == null) { + if (lt.isSleeping) { + lt.hp += 3 + (lt.cons + lt.consbon); + if (lt.hp > (lt.maxhp + lt.hpbon)) { + lt.hp = (lt.maxhp + lt.hpbon); + } + lt.mp += 3 + (lt.wisd + lt.wisdbon); + if (lt.mp > (lt.maxmp + lt.mpbon)) { + lt.mp = (lt.maxmp + lt.mpbon); + } + } else { + lt.hp += 1 + ((lt.cons + lt.consbon) / 2); + if (lt.hp > (lt.maxhp + lt.hpbon)) { + lt.hp = (lt.maxhp + lt.hpbon); + } + lt.mp += 1 + ((lt.wisd + lt.wisdbon) / 2); + if (lt.mp > (lt.maxmp + lt.mpbon)) { + lt.mp = (lt.maxmp + lt.mpbon); + } + } + lt.updateInfo(); + lt.savePlayer(); + } + } + } + //End of code submitted by Randall Leeds + + if (tick % 10 == 0) { + for (i = 0; i < battleList.size(); i++) { + batStore = (Battle) battleList.get(i); + if (batStore.blnRunning == false) { + battleList.remove(i); + i--; + } else { + batStore.run(); + } + } + } + if (tick > 72) { + tick = 0; + synchronized (mobList) { + for (Mob mob : mobList) { + if (mob.battle == null) { + // FIXME: magic number + if (mob.x != -6) { + mob.hp += 1 + (mob.cons / 2); + if (mob.hp > mob.maxhp) { + mob.hp = mob.maxhp; + } + mob.mp += 1 + (mob.wisd / 2); + if (mob.mp > mob.maxmp) { + mob.mp = mob.maxmp; + } + } else { + mob.hp++; + if (mob.hp > 0) { + mob.hp = mob.maxhp; + mob.mp = mob.maxmp; + mob.changeLocBypass(mob.originalX, mob.originalY); + } + } + } + } + } + for (Faction f : factionMap.values()) { + f.saveFactionData();; + } + } + tick++; + } catch (Exception e) { + log.printError("DuskEngine.run():at ticks", e); + } + } + } + + /** + * Script helpers + */ + public boolean canSeeLivingThing(LivingThing seeing, LivingThing seen) { + if (scrCanSeeLivingThing != null) { + synchronized (scrCanSeeLivingThing) { + scrCanSeeLivingThing.varVariables.clearVariables(); + scrCanSeeLivingThing.varVariables.addVariable("seeing", seeing); + scrCanSeeLivingThing.varVariables.addVariable("seen", seen); + return scrCanSeeLivingThing.rewindAndParseScript(); + } + } else { + return true; + } + } + + public boolean canMoveThrougLivingThing(LivingThing moving, LivingThing blocking) { + if (scrCanMoveThroughLivingThing != null) { + synchronized (scrCanMoveThroughLivingThing) { + scrCanMoveThroughLivingThing.varVariables.clearVariables(); + scrCanMoveThroughLivingThing.varVariables.addVariable("moving", moving); + scrCanMoveThroughLivingThing.varVariables.addVariable("blocking", blocking); + return scrCanMoveThroughLivingThing.rewindAndParseScript(); + } + } else { + return true; + } + } + + public void onStart(LivingThing trigger) { + try { + if (scrOnStart != null) { + synchronized (scrOnStart) { + scrOnStart.varVariables.clearVariables(); + scrOnStart.varVariables.addVariable("trigger", trigger); + scrOnStart.runScript(); + } + } + } catch (Exception e) { + } + } + + /** + * Temporarily ban an ip address + * + * @param strIP + */ + public void banAddress(String strIP) { + synchronized (bannedIPMap) { + bannedIPMap.put(strIP.toLowerCase(), System.currentTimeMillis()); + } + } + + public boolean isBanned(String ip) { + synchronized (bannedIPMap) { + return bannedIPMap.containsKey(ip.toLowerCase()); + } + } + + public void clearBanned() { + synchronized (bannedIPMap) { + bannedIPMap.clear();; + } + } +} diff --git a/DuskServer/src/duskz/server/DuskServer.java b/DuskServer/src/duskz/server/DuskServer.java new file mode 100644 index 0000000..6612f00 --- /dev/null +++ b/DuskServer/src/duskz/server/DuskServer.java @@ -0,0 +1,121 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Special thanks to: + * Randall Leeds + * Vittorio Alberto Floris + * Ian Macphail + * + * Changes + */ +package duskz.server; + +import duskz.server.entity.LivingThing; +import java.net.Socket; +import java.net.ServerSocket; + +/** + * DuskServer contains the main method for the game. + * It handles incoming network connections and passes + * them to DuskEngine. + * + * @author Tom Weingarten + */ +public class DuskServer implements Runnable { + + ServerSocket srvServer; + Socket sckStore; + DuskEngine engGame = null; + TrackerThread tracker = null; + SaveThread savThread = null; + TickThread ticks = null; + Thread thrEngine = null, + thrTracker = null, + thrSave = null, + thrTicks = null; + + /** + * Creates a new DuskServer object; + */ + public static void main(String args[]) { + System.out.println("Loading..."); + DuskServer MainServer = new DuskServer(); + Thread thrAccept = new Thread(MainServer); + thrAccept.setName("DuskServer"); + System.out.println("Ready for connections."); + thrAccept.start(); + } + + /** + * Creates a DuskEngine object, then a ServerSocket object, then + * starts a new thread to accept incoming connections. + */ + public DuskServer() { + engGame = new DuskEngine(); + thrEngine = new Thread(engGame); + thrEngine.setName("DuskEngine"); + thrEngine.start(); + if (engGame.tracker) { + tracker = new TrackerThread(engGame); + thrTracker = new Thread(tracker); + thrTracker.setName("Tracker"); + thrTracker.start(); + } + savThread = new SaveThread(engGame); + thrSave = new Thread(savThread); + thrSave.setName("Save"); + thrSave.start(); + ticks = new TickThread(engGame); + thrTicks = new Thread(ticks); + thrTicks.setName("Ticks"); + thrTicks.start(); + + try { + srvServer = new ServerSocket(engGame.port, 25); + } catch (Exception e) { + engGame.log.printError("DuskServer():Creating server socket", e); + engGame.log.printMessage(Log.ALWAYS, "Shutting Down"); + System.exit(0); + } + } + + /** + * Accepts incoming connections. + */ + public void run() { + LivingThing thnStore = null; + while (true) { + try { + sckStore = srvServer.accept(); + sckStore.setSoTimeout(30000); + + thnStore = new LivingThing(sckStore, engGame); + Thread thrLivingThing = new Thread(thnStore); + thrLivingThing.setName("LivingThing(new)"); + thrLivingThing.start(); + } catch (Exception e) { + engGame.log.printError("DuskServer.run()", e); + engGame.log.printMessage(Log.ALWAYS, "Shutting Down"); + System.exit(0); + return; + } + } + } +} diff --git a/DuskServer/src/duskz/server/Faction.java b/DuskServer/src/duskz/server/Faction.java new file mode 100644 index 0000000..b48e814 --- /dev/null +++ b/DuskServer/src/duskz/server/Faction.java @@ -0,0 +1,321 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.server; + +import duskz.server.entity.TileMap.MapData; +import duskz.server.entity.Mob; +import duskz.server.entity.LivingThing; +import duskz.server.entity.DuskObject; +import java.io.*; +import java.util.Vector; +import java.lang.Math; + +/** + * a Faction represents a group of mobs. + * + * @author Tom Weingarten + */ +public class Faction { + + final public String strName; + Vector vctRelations; + DuskEngine game; + boolean blnHasChanged = false; + + public Faction(String inName, DuskEngine inGame) { + strName = inName; + game = inGame; + vctRelations = new Vector(0); + parseFactionData(); + } + + void parseFactionData() { + RandomAccessFile rafFile = null; + try { + rafFile = new RandomAccessFile("factions/" + strName, "r"); + String strStore = rafFile.readLine(); + while (!(strStore == null || strStore.equals("."))) { + if (strStore.equalsIgnoreCase("relation")) { + vctRelations.addElement(new Relation(rafFile.readLine().toLowerCase(), Double.valueOf(rafFile.readLine()).doubleValue())); + } + strStore = rafFile.readLine(); + } + rafFile.close(); + } catch (Exception e) { + game.log.printError("parseFactionData()", e); + } + } + + synchronized void saveFactionData() { + /* + ** Only save faction data if it has changed. + */ + if (!blnHasChanged) { + return; + } + int i; + RandomAccessFile rafFile; + try { + rafFile = new RandomAccessFile("factions/" + strName, "rw"); + Relation relStore; + for (i = 0; i < vctRelations.size(); i++) { + relStore = (Relation) vctRelations.elementAt(i); + rafFile.writeBytes("relation\n" + relStore.strName.toLowerCase() + "\n" + relStore.dblLevel + "\n"); + } + rafFile.writeBytes(".\n"); + rafFile.close(); + } catch (Exception e) { + game.log.printError("saveFactionData()", e); + } + blnHasChanged = false; + } + + double getRelationValue(String strName) { + Relation relStore; + for (int i = 0; i < vctRelations.size(); i++) { + relStore = (Relation) vctRelations.elementAt(i); + if (strName.equalsIgnoreCase(relStore.strName)) { + if (i > 100) { + vctRelations.removeElementAt(i); + vctRelations.insertElementAt(relStore, 0); + } + return relStore.dblLevel; + } + } + return 0; //if no set relation, impartial + } + + Relation getRelation(String strName) { + Relation relStore; + for (int i = 0; i < vctRelations.size(); i++) { + relStore = (Relation) vctRelations.elementAt(i); + if (strName.equalsIgnoreCase(relStore.strName)) { + if (i > 100) { + vctRelations.removeElementAt(i); + vctRelations.insertElementAt(relStore, 0); + } + return relStore; + } + } + return null; + } + + void killedBy(LivingThing thnStore, LivingThing thnAttacker) { + if (game.blnAI) { + double dblOldLevel; + Relation relStore = getRelation(thnAttacker.name); + if (relStore == null) { + relStore = new Relation(thnAttacker.name, 0); + vctRelations.addElement(relStore); + dblOldLevel = 2; + } else { + dblOldLevel = relStore.dblLevel; + } + int delta = thnAttacker.getCharacterPoints() - thnStore.getCharacterPoints(); + /* Uses an optimized form of the function: + ((1.03^delta) + (1.03^-delta)) / (2 + (1.03^delta) + (1.03^-delta)) + */ + if (delta == 0) { + relStore.dblLevel -= (.5) * (1D + relStore.dblLevel); + } else if (delta > 0) { + relStore.dblLevel -= ((((Math.pow(1.03, delta)) / (Math.pow(1.03, delta) + 2))) / 2) * (1 + relStore.dblLevel); + } else { + relStore.dblLevel -= ((Math.pow(1.03, (-1 * delta)) / (Math.pow(1.03, (-1 * delta)) + 2)) / 2) * (1 + relStore.dblLevel); + } + if (!(thnAttacker.clan == null || thnAttacker.clan.equals("none"))) { + relStore = getRelation(thnAttacker.clan); + if (relStore == null) { + relStore = new Relation(thnAttacker.clan, 0); + vctRelations.addElement(relStore); + } + if (delta == 0) { + relStore.dblLevel -= (.5) * (1 + relStore.dblLevel); + } else if (delta > 0) { + relStore.dblLevel -= ((((Math.pow(1.03, delta)) / (Math.pow(1.03, delta) + 1)) + 1) / 2) * (1 + relStore.dblLevel); + } else { + relStore.dblLevel -= ((((Math.pow(1.03, (-1 * delta))) / (Math.pow(1.03, (-1 * delta) + 1))) + 1) / 2) * (1 + relStore.dblLevel); + } + } + if (relStore.dblLevel != dblOldLevel) { + blnHasChanged = true; + } + } + } + + void runAI(Mob mob) { + if (!game.blnAI) { + mob.blnCanSeePlayer = false; + return; + } + + //Battle AI (inside Battle class) + if (mob.battle != null) { + return; + } + + // FIXME: can this be moved to DuskEngine somehow? + + //Default AI + int intConfidence = 0; + LivingThing enemy = null; + Mob mobStore; + double enemyrelation = 0; + boolean visiblePlayer = false; + for (MapData md : game.map.range(mob.x, mob.y, game.viewrange - 1)) { + for (DuskObject o : md.entities) { + if (o.isLivingThing()) { + LivingThing lt = (LivingThing) o; + boolean visible = game.canSeeLivingThing(mob, lt); + + if (visible && game.canSeeTo(mob, lt.x, lt.y)) { + if (lt.isPlayer()) { + visiblePlayer = true; + double relation = getRelationValue(lt.name); + if (!(lt.clan == null || lt.clan.equals("none"))) { + relation = (relation + getRelationValue(lt.clan)) / 2; + } + if (relation < 0) { + if (enemy == null) { + enemy = lt; + enemyrelation = relation; + } else { + if (enemyrelation < relation) { + enemy = lt; + enemyrelation = relation; + } + } + } + intConfidence += relation * lt.getTotalPoints(); + } + if (lt.isMob()) { + double relation; + + mobStore = (Mob) lt; + if (mobStore.name.equals(mob.name)) { + relation = mobStore.dblGroupRelation; + } else { + relation = getRelationValue(mobStore.name); + } + intConfidence += relation * mobStore.getTotalPoints(); + if (relation < 0) { + if (enemy == null) { + enemy = mobStore; + enemyrelation = relation; + } else { + if (enemyrelation > relation) { + enemy = mobStore; + enemyrelation = relation; + } + } + } + intConfidence += relation * lt.getTotalPoints(); + } + } + } + } + } + if (!visiblePlayer) { + mob.blnCanSeePlayer = false; + return; + } + mob.blnCanSeePlayer = true; + if (enemy != null) { + double delta = (mob.getTotalPoints() + intConfidence) * mob.dblBravery * -1 * enemyrelation; + int enemycp = enemy.getTotalPoints(); + //Fight/flee + if (enemycp < delta) { + if (enemycp > delta - (delta * 0.1 * Math.random())) { + return; + } + //Fight + if (enemy.tileDistance(mob) <= mob.getRangeWithBonus()) { + System.out.println(mob.name + " close enough, going into battle distance: " + enemy.distance(mob) + " range: " + mob.getRangeWithBonus()); + // close enough to attack, so stop moving + mob.clearMoveQueue(); + game.newBattle(mob, enemy); + /* + try { + // TODO: just call duskEngine.newBattle directly? + Commands.parseCommand(mob, game, "a " + enemy.name); + } catch (Exception e) { + game.log.printError("runAI():" + mob.name + " had an error attacking " + enemy.name, e); + }*/ + } else { + mob.goTo(enemy.x, enemy.y, false); + } + } else { + if (enemycp < delta + (delta * 0.1 * Math.random())) { + return; + } + //Flee + int destX; + int destY; + if (enemy.x > mob.x) { + destX = mob.x - game.viewrange; + } else { + destX = mob.x + game.viewrange; + } + if (enemy.y > mob.y) { + destY = mob.y - game.viewrange; + } else { + destY = mob.y + game.viewrange; + } + mob.goTo(destX, destY, true); + } + } + + //If no enemies + if ((int) (Math.random() * 25) == 1) { + if ((int) (Math.random() * 2) == 1) { + if ((int) (Math.random() * 2) == 1) { + mob.moveE(); + } else { + mob.moveW(); + } + } else { + if ((int) (Math.random() * 2) == 1) { + mob.moveS(); + } else { + mob.moveN(); + } + } + } + } +} + +/** + * a Relation represents a feeling held by one faction for another faction, a + * player, or a clan. The mob AI bases it's decisions around Relations. + * + * @author Tom Weingarten + */ +class Relation { + + String strName; + double dblLevel = 0; //-1 to 1 + + Relation(String inName, double inLevel) { + strName = inName; + dblLevel = inLevel; + } +} diff --git a/DuskServer/src/duskz/server/GiveItem.java b/DuskServer/src/duskz/server/GiveItem.java new file mode 100644 index 0000000..976cf11 --- /dev/null +++ b/DuskServer/src/duskz/server/GiveItem.java @@ -0,0 +1,42 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.server; + +/** +*GiveItem gives an Item to a LivingThing after they kill a mob. +* +*@author Tom Weingarten +*/ + +public class GiveItem +{ + String strItemName; + double dblProbability; + + public GiveItem(String strName, double dblProb) + { + strItemName = strName; + dblProbability = dblProb; + } +} diff --git a/DuskServer/src/duskz/server/ItemList.java b/DuskServer/src/duskz/server/ItemList.java new file mode 100644 index 0000000..0fab2ff --- /dev/null +++ b/DuskServer/src/duskz/server/ItemList.java @@ -0,0 +1,95 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - attempt at cleanup using modern collections. + */ +package duskz.server; + +import duskz.server.entity.Item; +import java.util.HashMap; +import java.util.LinkedList; + +/** + * Holds all items, keyed by the item type. + */ +public class ItemList extends HashMap> { + + public boolean contains(String strItemName) { + return containsKey(strItemName.toLowerCase()); + } + + public void addElement(Item item) { + String key = item.name.toLowerCase(); + if (contains(key)) { + get(key).push(item); + } else { + LinkedList list = new LinkedList<>(); + list.push(item); + put(key, list); + } + } + + public Item removeElement(String name) { + name = name.toLowerCase(); + LinkedList list = get(name); + if (list != null && !list.isEmpty()) { + Item item = list.pop(); + if (list.isEmpty()) { + remove(name); + } + return item; + } + return null; + } + + /* + ** This method formats the ItemList for sending to the client + ** for display of the player's inventory to the player. + */ + public String print() { + StringBuilder invBuffer = new StringBuilder(); + for (String name : keySet()) { + LinkedList list = get(name); + Item item = list.element(); + + invBuffer.append("").append((char) 3).append(list.size()).append(" ").append(item.name).append("\n"); + } + return invBuffer.toString(); + } + + /* + ** This method formats the ItemList for saving. + ** It generates a String that can later be passed + ** to fromString for populating a new ItemList. + */ + public String toString() { + StringBuilder invBuffer = new StringBuilder(); + for (String name : keySet()) { + LinkedList list = get(name); + + for (Item item : list) { + invBuffer.append(item.toString()).append("/\n"); + } + } + return invBuffer.toString(); + } +} diff --git a/DuskServer/src/duskz/server/Log.java b/DuskServer/src/duskz/server/Log.java new file mode 100644 index 0000000..4d3f89f --- /dev/null +++ b/DuskServer/src/duskz/server/Log.java @@ -0,0 +1,136 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.server; + +import java.io.PrintStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + + +public class Log +{ + public static final int ALWAYS = 0; + public static final int INFO = 1; + public static final int ERROR = 2; + public static final int VERBOSE = 3; + public static final int DEBUG = 4; + + + private static final String MSG_ALWAYS = "ALWAYS"; + private static final String MSG_INFO = "INFO"; + private static final String MSG_ERROR = "ERROR"; + private static final String MSG_VERBOSE = "VERBOSE"; + private static final String MSG_DEBUG = "DEBUG"; + private static final String LOG_SEP = "::"; + private PrintStream psOut; + private int logLevel = ERROR; + private SimpleDateFormat formatter; + + public Log(PrintStream ps) + { + formatter = new SimpleDateFormat("EEE MMM dd hh:mm:ss yyyy", Locale.getDefault()); + if (ps != null) + { + psOut = ps; + } else + { + psOut = System.out; + } + } + + public void setLogLevel(int newLogLevel) + { + if ((newLogLevel >= ALWAYS) && (newLogLevel <= DEBUG)) + { + logLevel = newLogLevel; + } + } + + public int getLogLevel() + { + return logLevel; + } + + private void printTimeStamp(int level) + { + switch(level) + { + case ALWAYS: + { + psOut.print(MSG_ALWAYS+LOG_SEP); + break; + } + case ERROR: + { + psOut.print(MSG_ERROR+LOG_SEP); + break; + } + case INFO: + { + psOut.print(MSG_INFO+LOG_SEP); + break; + } + case VERBOSE: + { + psOut.print(MSG_VERBOSE+LOG_SEP); + break; + } + case DEBUG: + { + psOut.print(MSG_DEBUG+LOG_SEP); + break; + } + } + psOut.print(formatter.format(new Date())+LOG_SEP); + if (logLevel >= DEBUG) + { + psOut.print("thread="+Thread.currentThread().getName()+LOG_SEP); + } + } + + public void printMessage(int level, String strMessage) + { + if (level <= logLevel) + { + printTimeStamp(level); + psOut.println(strMessage); + } + } + + public void printError(String strMessage, Exception e) + { + if (logLevel >= ERROR) + { + printTimeStamp(ERROR); + psOut.print(strMessage+LOG_SEP); + if (logLevel >= DEBUG) + { + e.printStackTrace(psOut); + } else + { + psOut.println(e.toString()); + } + } + } +} diff --git a/DuskServer/src/duskz/server/RandomAccessString.java b/DuskServer/src/duskz/server/RandomAccessString.java new file mode 100644 index 0000000..e91fd07 --- /dev/null +++ b/DuskServer/src/duskz/server/RandomAccessString.java @@ -0,0 +1,109 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +package duskz.server; + +import java.io.*; + +// TODO: wont be needed with javascript +@Deprecated +public class RandomAccessString +{ + private String strContent = null; + private String strCurrentContent = null; + private int lCurrentPosition; + + public RandomAccessString(String strFileName) + throws IOException + { + lCurrentPosition = -1; + File filView = new File(strFileName); + RandomAccessFile rafFile = new RandomAccessFile(filView, "r"); + byte [] buffer = new byte[(int)rafFile.length()]; + rafFile.readFully(buffer); + rafFile.close(); + strContent = new String(buffer); + strCurrentContent = strContent; + lCurrentPosition = 0; + } + + public void close() + { + strContent = null; + strCurrentContent = null; + } + + public int getFilePointer() + { + return lCurrentPosition; + } + + public void seek(int lSeekPosition) + { + if (lSeekPosition >= 0 && lSeekPosition < strContent.length()) + { + lCurrentPosition = lSeekPosition; + strCurrentContent = strContent.substring(lCurrentPosition); + } + } + + public String readLine() + { + if (lCurrentPosition == -1 || strCurrentContent == null) + { + return null; + } + String strReturn; + int newline = strCurrentContent.indexOf("\r"); + if (newline == -1) + { + newline = strCurrentContent.indexOf("\n"); + if (newline == -1) + { + strReturn = strCurrentContent; + strCurrentContent = null; + lCurrentPosition = -1; + return strReturn; + } + } + strReturn = strCurrentContent.substring(0, newline); + while((newline < strCurrentContent.length()) + && ((strCurrentContent.charAt(newline) == '\r') + || (strCurrentContent.charAt(newline) == '\n'))) + { + newline++; + } + lCurrentPosition += newline; + strCurrentContent = strCurrentContent.substring(newline); + return strReturn; + } + + public byte readByte() + throws IOException + { + if (lCurrentPosition == -1) + { + throw new IOException(); + } + byte bReturn = (byte)strContent.charAt(lCurrentPosition); + lCurrentPosition++; + strCurrentContent = strContent.substring(lCurrentPosition); + return bReturn; + } +} diff --git a/DuskServer/src/duskz/server/SaveThread.java b/DuskServer/src/duskz/server/SaveThread.java new file mode 100644 index 0000000..328a238 --- /dev/null +++ b/DuskServer/src/duskz/server/SaveThread.java @@ -0,0 +1,47 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.server; + +public class SaveThread implements Runnable { + + DuskEngine engGame; + + SaveThread(DuskEngine inGame) { + engGame = inGame; + } + + public void run() { + while (true) { + try { + Thread.currentThread().sleep(3600000); // sleep for an hour + engGame.saveMap(); // save the game + + // Clear out the auto banned IP addresses once an hour + engGame.clearBanned(); + + } catch (Exception e) { + engGame.log.printError("SaveThread.run()", e); + } + } + } +} \ No newline at end of file diff --git a/DuskServer/src/duskz/server/Script.java b/DuskServer/src/duskz/server/Script.java new file mode 100644 index 0000000..7e9dc6e --- /dev/null +++ b/DuskServer/src/duskz/server/Script.java @@ -0,0 +1,2011 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +package duskz.server; + +/* + Portions of code relating to getString() and script + parameters by Randall Leeds and Tom Weingarten + */ +import duskz.server.entity.Mob; +import duskz.server.entity.LivingThing; +import duskz.server.entity.Item; +import duskz.server.entity.DuskObject; +import java.io.*; +import java.util.Vector; +import java.lang.Math; +import java.util.List; + +/** + * Script reads and executes DuskScripts. + * + * @deprecated is to be replaced by JavaScript + * @author Tom Weingarten + */ +@Deprecated +public class Script { + + Vector vctUpdate; + Vector vctVisibleUpdate; + public VariableSet varVariables; +// RandomAccessFile rafScript; + RandomAccessString rafScript; + DuskEngine engGame; + String strRead = null; + //for script debugging purposes: + String strName; + int intLine = 0; +// long lngQuoteFilePointer; + int lngQuoteFilePointer; + + public Script(String strIn, DuskEngine inEngine, boolean forcecompile) + throws Exception { + engGame = inEngine; + strName = strIn; + if (forcecompile) { + File filDelete = new File(strIn + ".dsko"); + if (filDelete.exists()) { + filDelete.delete(); + } + compileScript(strIn); + } else { + File filCompileCheck = new File(strIn + ".dsko"); + if (!filCompileCheck.exists()) { + compileScript(strIn); + } + } + varVariables = new VariableSet(); +// rafScript = new RandomAccessFile(strIn+".dsko","r"); + rafScript = new RandomAccessString(strIn + ".dsko"); + intLine = 0; + } + + /** + * Helper to run a script with only a trigger arg. + * + * Silently ignores missing scripts. + * + * @param name + * @param trigger + */ + public static void exec(String name, DuskEngine engGame, LivingThing trigger) { + File file = new File(name); + + if (!file.exists()) + return; + + Script script = null; + + try { + script = new Script(name, engGame, false); + script.varVariables.addVariable("trigger", trigger); + script.runScript(); + } catch (Exception e) { + engGame.log.printError("Executing script " + name, e); + } finally { + if (script != null) { + try { + script.close(); + } catch (Exception e) { + } + } + } + } + // TODO: work out what i want here/clean up every caller + + public static void exec(String name, DuskEngine engGame) { + File file = new File(name); + + if (!file.exists()) + return; + + Script script = null; + + try { + script = new Script(name, engGame, false); + script.runScript(); + } catch (Exception e) { + engGame.log.printError("Executing script " + name, e); + } finally { + if (script != null) { + try { + script.close(); + } catch (Exception e) { + } + } + } + } + + synchronized void compileScript(String name) + throws Exception { + RandomAccessFile rafCompile; +// rafScript = new RandomAccessFile(name, "r"); + rafScript = new RandomAccessString(name); + try { + rafCompile = new RandomAccessFile(name + ".dsko", "rw"); + } catch (Exception e) { + engGame.log.printError("compileScript():Opening " + name + ".dsko file for script " + name, e); + return; + } + try { + while (parseScriptForCompile(rafCompile)) { + } + } catch (Exception e) { + engGame.log.printError("compileScript():While running parseScriptForCompile() for script " + name, e); + } + try { + rafCompile.close(); + } catch (Exception e) { + engGame.log.printError("compileScript():While closing .dsko file for script " + name, e); + } + } + + public void close() { + try { + rafScript.close(); + } catch (Exception e) { + engGame.log.printError("Script.close():Closing script " + strName, e); + } + } + + LivingThing getLivingThing(String strName) { + try { + LivingThing thnStore; + if (strName.equalsIgnoreCase("player")) { + return engGame.getPlayer(getString()); + } else if (strName.equalsIgnoreCase("local")) { + DuskObject objStore = getLivingThing(getString()).getLocalObject(getString()); + if (objStore != null && objStore.isLivingThing()) { + return ((LivingThing) objStore); + } + return null; + } else if (strName.equalsIgnoreCase("enemy")) { + thnStore = getLivingThing(getString()); + return Battle.getEnemy(thnStore); + } else if (strName.equalsIgnoreCase("pet")) { + thnStore = getLivingThing(getString()); + if (thnStore.getFollowing() != null && thnStore.getFollowing().isPet()) { + return thnStore.getFollowing(); + } + return null; + } else if (strName.equalsIgnoreCase("following")) { + thnStore = getLivingThing(getString()); + if (thnStore.getFollowing() != null && thnStore.getFollowing().isPet()) { + return thnStore.getFollowing(); + } else { + return null; + } + } else if (strName.equalsIgnoreCase("global")) { + Variable varStore = engGame.varVariables.getVariable(readScriptForCompile()); + if (varStore != null && varStore.isLivingThing()) { + return (LivingThing) varStore.objData; + } + } + Variable varStore = varVariables.getVariable(strName); + if (varStore != null && varStore.isLivingThing()) { + return (LivingThing) varStore.objData; + } + thnStore = engGame.getPlayer(strName); + if (thnStore != null) { + return thnStore; + } + thnStore = engGame.getMobFromVct(strName); + if (thnStore != null) { + return thnStore; + } + thnStore = engGame.getPet(strName); + if (thnStore != null) { + return thnStore; + } + } catch (Exception e) { + engGame.log.printError("getLivingThing()", e); + } + return null; + } + + Double getNumber(String strName) { + try { + return Double.valueOf(strName); + } catch (NumberFormatException e) { + } + if (strName.equalsIgnoreCase("global")) { + try { + Variable varStore = engGame.varVariables.getVariable(readScriptForCompile()); + if (varStore != null && varStore.isNumber()) { + return (Double) varStore.objData; + } + } catch (Exception e) { + return null; + } + } + Variable varStore = varVariables.getVariable(strName); + if (varStore != null && varStore.isNumber()) { + return (Double) varStore.objData; + } + return null; + } + + private void rewindScript() { + intLine = 0; + strRead = null; + try { + rafScript.seek(0); + } catch (Exception e) { + engGame.log.printError("rewindScript()", e); + } + } + + public void runScript() { + rewindScript(); + vctUpdate = new Vector(0); + vctVisibleUpdate = new Vector(0); + while (parseScript()) { + } + LivingThing thnStore; + for (int i = 0; i < vctUpdate.size(); i++) { + thnStore = (LivingThing) vctUpdate.elementAt(i); + thnStore.updateStats(); + } + for (int i = 0; i < vctVisibleUpdate.size(); i++) { + thnStore = (LivingThing) vctVisibleUpdate.elementAt(i); + engGame.removeDuskObject(thnStore); + engGame.addDuskObject(thnStore); + } + } + + public void runScript(String strParams) { + String strStore2; + int i2; + try { + for (int i = 0; !strParams.equals(""); i++) { + if (strParams.startsWith("\"")) { + char c, c2; + + strStore2 = ""; + i2 = 1; + while (true) { + c = strParams.charAt(i2); + if (c == '\\') { + c2 = strParams.charAt(i2 + 1); + strStore2 += c2; + } else if (c == '\"') { + i2++; + break; + } else { + strStore2 += c; + } + i2++; + } + } else { + i2 = strParams.indexOf(" "); + if (i2 == -1) { + strStore2 = strParams; + varVariables.addVariable("param" + i, strStore2); + break; + } else { + strStore2 = strParams.substring(0, i2); + } + } + varVariables.addVariable("param" + i, strStore2); + if (strParams.length() > i2) { + strParams = strParams.substring(i2 + 1); + } else { + strParams = ""; + } + } + } catch (Exception e) { + engGame.log.printError("runScript()", e); + } + runScript(); + } + + public boolean rewindAndParseScript() { + rewindScript(); + return parseScript(); + } + + String readScriptForCompile() + throws IOException { + String strStore2; + if (strRead == null) { + // initialize quotation marker before read as we do not know whether + // readLine is stripping a cr or a cr/lf pair. This eliminates the + // need to use strRead.length() in the calculation. + lngQuoteFilePointer = rafScript.getFilePointer(); + strRead = rafScript.readLine(); + if (strRead == null) { + return null; + } + // Scan for quotation marks before we can clean up the string with trim + int q = strRead.indexOf("\""); + if (q != -1) { + lngQuoteFilePointer += q + 1; + } + strRead = strRead.trim(); + intLine++; + } + int i = strRead.indexOf(" "); + if (i == -1) { + strStore2 = strRead; + strRead = null; + return strStore2; + } + strStore2 = strRead.substring(0, i).trim(); + try { + strRead = strRead.substring(i + 1, strRead.length()); + } catch (Exception e) { + strRead = null; + } + return strStore2; + } + + String getStringForCompile() + throws IOException { + String strStore; + strStore = readScriptForCompile(); + + if (strStore.equals("concat")) { + return "concat " + getStringForCompile() + getStringForCompile(); + } + + if (strStore.equalsIgnoreCase("name")) { + return "name " + getStringForCompile(); + } + + if (strStore.equalsIgnoreCase("enemy")) { + return "enemy " + getStringForCompile(); + } + + if (strStore.equalsIgnoreCase("following")) { + return "following " + getStringForCompile(); + } + + if (strStore.equalsIgnoreCase("clan")) { + return "clan " + getStringForCompile(); + } + + if (strStore.startsWith("\"")) { + char c, c2; + //Go back to the beginning of the quote + rafScript.seek(lngQuoteFilePointer); + + //Read quote into strStore2 + strStore = "\""; + while (true) { + c = (char) rafScript.readByte(); + if (c == '\\') { + c2 = (char) rafScript.readByte(); + strStore += "\\"; + strStore += c2; + } else if (c == '\"') { + break; + } else if (c == '\n') { + strStore += "\n"; + intLine++; + } else if (c == '\r') { + strStore += "\n"; + intLine++; + // Skip over LF as this was a CR/LF pair + c2 = (char) rafScript.readByte(); + if (c2 != '\n') { + // Back up 1 char if this wasn't a CR/LF pair + rafScript.seek(rafScript.getFilePointer() - 1); + } + } else //If no special characters exist, add it to the string + { + strStore += c; + } + } + strRead = null; //Delete strRead, readScriptForCompile will recreate it + strStore += "\""; + return strStore; + } else { + if (strStore.equalsIgnoreCase("global")) { + strStore = getStringForCompile(); + return "global " + strStore; + } + } + return strStore + " "; + } + + String readScript() + throws IOException { + String strStore = ""; + char c = (char) rafScript.readByte(); + while (c != ' ') { + strStore += c; + c = (char) rafScript.readByte(); + } + return strStore; + } + + String getString() + throws IOException { + String strStore = ""; +// long lngOldFilePointer; + int lngOldFilePointer; + + lngOldFilePointer = rafScript.getFilePointer(); + + char c = (char) rafScript.readByte(); + + //Skip over leading spaces + while (c == ' ') { + lngOldFilePointer++; + c = (char) rafScript.readByte(); + } + + if (c == '\"') { + c = (char) rafScript.readByte(); + while (c != '\"') { + strStore += c; + c = (char) rafScript.readByte(); + } + } else { + while (c != ' ') { + strStore += c; + c = (char) rafScript.readByte(); + } + } + + if (strStore.equals("concat")) { + return getString() + getString(); + } + + if (strStore.equalsIgnoreCase("name")) { + LivingThing thnStore = getLivingThing(getString()); + if (thnStore != null) { + return thnStore.name; + } + return "name"; + } + + if (strStore.equalsIgnoreCase("clan")) { + LivingThing thnStore = getLivingThing(getString()); + if (thnStore != null) { + return thnStore.clan; + } + return "clan"; + } + + if (strStore.startsWith("\"")) { + char c2; + //Go back to the beginning of the quote + rafScript.seek(lngOldFilePointer + 1); + + //Read quote into strStore2 + strStore = ""; + while (true) { + c = (char) rafScript.readByte(); + if (c == '\\') { + c2 = (char) rafScript.readByte(); + strStore += c2; + } else if (c == '\"') { + break; + } else //If no special characters exist, add it to the string + { + strStore += c; + } + } +// try +// { +// rafScript.seek(rafScript.getFilePointer()+1);// Why do I need this to skip over ending quote? +// }catch(EOFException e){} + } else { + Variable varStore = null; + if (strStore.equalsIgnoreCase("global")) { + strStore = getString(); + varStore = engGame.varVariables.getVariable(strStore); + } else { + varStore = varVariables.getVariable(strStore); + } + if (varStore == null) { + return strStore; + } else if (varStore.isString()) { + return (String) varStore.objData; + } else if (varStore.isLivingThing()) { + return strStore; + } else if (varStore.isNumber()) { + return String.valueOf(((Double) varStore.objData).intValue()); + } + } + return strStore; + } + + boolean parseScriptForCompile(RandomAccessFile rafCompile) + throws Exception { + String strStore; + try { + strStore = readScriptForCompile(); + if (strStore == null) { + return false; + } + } catch (EOFException e) { + return false; + } + if (strStore.startsWith("#") || strStore.equals(" ")) //comments + { + strRead = null; + intLine++; + return true; + } + if (strStore.equals("!") || strStore.equalsIgnoreCase("not")) //Not, returns the opposite of the next line + { + rafCompile.writeByte(0); + return true; + } else if (strStore.equalsIgnoreCase("if")) { + rafCompile.writeByte(1); + return true; + } else if (strStore.equals("end")) { + rafCompile.writeByte(2); + return true; + } else if (strStore.equals(")") || strStore.equals("(")) { + return true; + } else if (strStore.equalsIgnoreCase("t") || strStore.equalsIgnoreCase("true")) { + rafCompile.writeByte(3); + return true; + } else if (strStore.equalsIgnoreCase("f") || strStore.equalsIgnoreCase("false")) { + rafCompile.writeByte(4); + return true; + } else if (strStore.equalsIgnoreCase("playsound")) { + rafCompile.writeByte(5); + rafCompile.writeBytes(parseValueForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("order")) { + rafCompile.writeByte(6); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("hascondition")) { + rafCompile.writeByte(7); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("addcondition")) { + rafCompile.writeByte(8); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("addconditionwithduration")) { + rafCompile.writeByte(9); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("removecondition")) { + rafCompile.writeByte(10); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("addflag")) { + rafCompile.writeByte(11); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("hasflag")) { + rafCompile.writeByte(12); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("removeflag")) { + rafCompile.writeByte(13); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("global")) { + strStore = getStringForCompile().trim(); + if (strStore.equalsIgnoreCase("livingthing")) { + rafCompile.writeByte(14); + rafCompile.writeBytes(readScriptForCompile() + " "); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("number")) { + rafCompile.writeByte(15); + rafCompile.writeBytes(readScriptForCompile() + " "); + rafCompile.writeBytes(parseValueForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("string")) { + rafCompile.writeByte(16); + rafCompile.writeBytes(readScriptForCompile() + " "); + rafCompile.writeBytes(getStringForCompile()); + return true; + } + } else if (strStore.equalsIgnoreCase("livingthing")) { + rafCompile.writeByte(17); + rafCompile.writeBytes(readScriptForCompile() + " "); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("number")) { + rafCompile.writeByte(18); + rafCompile.writeBytes(readScriptForCompile() + " "); + rafCompile.writeBytes(parseValueForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("string")) { + rafCompile.writeByte(19); + rafCompile.writeBytes(readScriptForCompile() + " "); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("or")) { + rafCompile.writeByte(20); + return true; + } else if (strStore.equalsIgnoreCase("and")) { + rafCompile.writeByte(21); + return true; + } else if (strStore.equalsIgnoreCase("<")) { + rafCompile.writeByte(22); + rafCompile.writeBytes(parseValueForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + return true; + } else if (strStore.equalsIgnoreCase(">")) { + rafCompile.writeByte(23); + rafCompile.writeBytes(parseValueForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("=")) { + rafCompile.writeByte(24); + rafCompile.writeBytes(parseValueForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("isinbattle")) { + rafCompile.writeByte(25); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("isclan")) { + rafCompile.writeByte(26); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("israce")) { + rafCompile.writeByte(27); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("hasitem")) { + rafCompile.writeByte(28); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("createmob")) { + rafCompile.writeByte(29); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("createoneusemob")) { + rafCompile.writeByte(30); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("createitem")) { + rafCompile.writeByte(31); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("giveitem")) { + rafCompile.writeByte(59); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("removeitem")) { + rafCompile.writeByte(33); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("iswearing")) { + rafCompile.writeByte(34); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("unwear")) { + rafCompile.writeByte(35); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("variableisnumber")) { + rafCompile.writeByte(36); + strStore = getStringForCompile(); + if (strStore.equalsIgnoreCase("global")) { + rafCompile.writeBytes("global "); + strStore = getStringForCompile(); + } + rafCompile.writeBytes(strStore); + return true; + } else if (strStore.equalsIgnoreCase("variableisstring")) { + rafCompile.writeByte(37); + strStore = getStringForCompile(); + if (strStore.equalsIgnoreCase("global")) { + rafCompile.writeBytes("global "); + strStore = getStringForCompile(); + } + rafCompile.writeBytes(strStore); + return true; + } else if (strStore.equalsIgnoreCase("variableislivingthing")) { + rafCompile.writeByte(38); + strStore = getStringForCompile(); + if (strStore.equalsIgnoreCase("global")) { + rafCompile.writeBytes("global "); + strStore = getStringForCompile(); + } + rafCompile.writeBytes(strStore); + return true; + } else if (strStore.equalsIgnoreCase("isplayer")) { + rafCompile.writeByte(39); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("ispet")) { + rafCompile.writeByte(40); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("ismob")) { + rafCompile.writeByte(41); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("chat")) { + rafCompile.writeByte(42); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("chatc")) { + rafCompile.writeByte(43); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("inc")) { + rafCompile.writeByte(44); + rafCompile.writeBytes(getStringForCompile()); + strStore = readScriptForCompile(); + if (strStore.equalsIgnoreCase("hp")) { + rafCompile.writeByte(0); + } + if (strStore.equalsIgnoreCase("maxhp")) { + rafCompile.writeByte(1); + } + if (strStore.equalsIgnoreCase("hpbonus")) { + rafCompile.writeByte(2); + } + if (strStore.equalsIgnoreCase("mp")) { + rafCompile.writeByte(3); + } + if (strStore.equalsIgnoreCase("maxmp")) { + rafCompile.writeByte(4); + } + if (strStore.equalsIgnoreCase("mpbonus")) { + rafCompile.writeByte(5); + } + if (strStore.equalsIgnoreCase("damdone")) { + rafCompile.writeByte(6); + } + if (strStore.equalsIgnoreCase("strength")) { + rafCompile.writeByte(7); + } + if (strStore.equalsIgnoreCase("intelligence")) { + rafCompile.writeByte(8); + } + if (strStore.equalsIgnoreCase("dexterity")) { + rafCompile.writeByte(9); + } + if (strStore.equalsIgnoreCase("wisdom")) { + rafCompile.writeByte(10); + } + if (strStore.equalsIgnoreCase("constitution")) { + rafCompile.writeByte(11); + } + if (strStore.equalsIgnoreCase("strengthbonus")) { + rafCompile.writeByte(12); + } + if (strStore.equalsIgnoreCase("intelligencebonus")) { + rafCompile.writeByte(13); + } + if (strStore.equalsIgnoreCase("dexteritybonus")) { + rafCompile.writeByte(14); + } + if (strStore.equalsIgnoreCase("wisdombonus")) { + rafCompile.writeByte(15); + } + if (strStore.equalsIgnoreCase("constitutionbonus")) { + rafCompile.writeByte(16); + } + if (strStore.equalsIgnoreCase("acbonus")) { + rafCompile.writeByte(17); + } + if (strStore.equalsIgnoreCase("dammodbonus")) { + rafCompile.writeByte(18); + } + if (strStore.equalsIgnoreCase("locx")) { + rafCompile.writeByte(19); + } + if (strStore.equalsIgnoreCase("locy")) { + rafCompile.writeByte(20); + } + if (strStore.equalsIgnoreCase("locxy")) { + rafCompile.writeByte(21); + rafCompile.writeBytes(parseValueForCompile()); + } + if (strStore.equalsIgnoreCase("exp")) { + rafCompile.writeByte(22); + } + if (strStore.equalsIgnoreCase("cash")) { + rafCompile.writeByte(23); + } + if (strStore.equalsIgnoreCase("skill")) { + rafCompile.writeByte(24); + rafCompile.writeBytes(getStringForCompile()); + } + rafCompile.writeBytes(parseValueForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("set")) { + rafCompile.writeByte(45); + rafCompile.writeBytes(getStringForCompile()); + strStore = readScriptForCompile(); + if (strStore.equalsIgnoreCase("hp")) { + rafCompile.writeByte(0); + } + if (strStore.equalsIgnoreCase("maxhp")) { + rafCompile.writeByte(1); + } + if (strStore.equalsIgnoreCase("hpbonus")) { + rafCompile.writeByte(2); + } + if (strStore.equalsIgnoreCase("mp")) { + rafCompile.writeByte(3); + } + if (strStore.equalsIgnoreCase("maxmp")) { + rafCompile.writeByte(4); + } + if (strStore.equalsIgnoreCase("mpbonus")) { + rafCompile.writeByte(5); + } + if (strStore.equalsIgnoreCase("damdone")) { + rafCompile.writeByte(6); + } + if (strStore.equalsIgnoreCase("strength")) { + rafCompile.writeByte(7); + } + if (strStore.equalsIgnoreCase("intelligence")) { + rafCompile.writeByte(8); + } + if (strStore.equalsIgnoreCase("dexterity")) { + rafCompile.writeByte(9); + } + if (strStore.equalsIgnoreCase("wisdom")) { + rafCompile.writeByte(10); + } + if (strStore.equalsIgnoreCase("constitution")) { + rafCompile.writeByte(11); + } + if (strStore.equalsIgnoreCase("strengthbonus")) { + rafCompile.writeByte(12); + } + if (strStore.equalsIgnoreCase("intelligencebonus")) { + rafCompile.writeByte(13); + } + if (strStore.equalsIgnoreCase("dexteritybonus")) { + rafCompile.writeByte(14); + } + if (strStore.equalsIgnoreCase("wisdombonus")) { + rafCompile.writeByte(15); + } + if (strStore.equalsIgnoreCase("constitutionbonus")) { + rafCompile.writeByte(16); + } + if (strStore.equalsIgnoreCase("acbonus")) { + rafCompile.writeByte(17); + } + if (strStore.equalsIgnoreCase("dammodbonus")) { + rafCompile.writeByte(18); + } + if (strStore.equalsIgnoreCase("locx")) { + rafCompile.writeByte(19); + } + if (strStore.equalsIgnoreCase("locy")) { + rafCompile.writeByte(20); + } + if (strStore.equalsIgnoreCase("locxy")) { + rafCompile.writeByte(21); + rafCompile.writeBytes(parseValueForCompile()); + } + if (strStore.equalsIgnoreCase("exp")) { + rafCompile.writeByte(22); + } + if (strStore.equalsIgnoreCase("cash")) { + rafCompile.writeByte(23); + } + if (strStore.equalsIgnoreCase("skill")) { + rafCompile.writeByte(24); + rafCompile.writeBytes(getStringForCompile()); + } + rafCompile.writeBytes(parseValueForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("changeTile")) { + rafCompile.writeByte(46); + rafCompile.writeBytes(parseValueForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + rafCompile.writeBytes(parseValueForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("strequals")) { + rafCompile.writeByte(47); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("input")) { + rafCompile.writeByte(48); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("checkviewable")) { + rafCompile.writeByte(49); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("log")) { + rafCompile.writeByte(50); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("gecho")) { + rafCompile.writeByte(51); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("endscript")) { + rafCompile.writeByte(4); + return true; + } else if (strStore.equalsIgnoreCase("chatwindow")) { + rafCompile.writeByte(52); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("kill")) { + rafCompile.writeByte(53); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("endcommand")) { + rafCompile.writeByte(54); + return true; + } else if (strStore.equalsIgnoreCase("clanleader")) { + rafCompile.writeByte(55); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("changeclan")) { + rafCompile.writeByte(56); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("floodoff")) { + rafCompile.writeByte(57); + rafCompile.writeBytes(getStringForCompile()); + return true; + } else if (strStore.equalsIgnoreCase("battlechat")) { + rafCompile.writeByte(58); + rafCompile.writeBytes(getStringForCompile()); + rafCompile.writeBytes(getStringForCompile()); + return true; + } + rafCompile.writeBytes(strStore + " "); + return true; + } + + boolean parseScript() { + intLine++; + byte bytCommand = 0; + try { + bytCommand = rafScript.readByte(); + // Skip over spaces between commands (if any) + while (bytCommand == 32) { + bytCommand = rafScript.readByte(); + } + switch (bytCommand) { + case 0: { + if (parseScript()) { + return false; + } + return true; + } + case 1: { + if (!parseScript()) { + int ifCount = 1; + byte bytStore; + while (true) { + bytStore = rafScript.readByte(); + if (bytStore == 1) { + ifCount++; + } else if (bytStore == 2) { + ifCount--; + if (ifCount == 0) { + break; + } + } + } + } + return true; + } + case 2: { + return true; + } + case 3: { + return true; + } + case 4: { + return false; + } + case 5: { + engGame.playSound((int) parseValue(), (int) parseValue(), (int) parseValue()); + return true; + } + case 6: { + LivingThing thnStore = getLivingThing(getString()); + Commands.parseCommand(thnStore, engGame, getString()); + return true; + } + case 7: { + return getLivingThing(getString()).hasCondition(getString()); + } + case 8: { + getLivingThing(getString()).addCondition(engGame.getCondition(getString())); + return true; + } + case 9: { + LivingThing thnStore = getLivingThing(getString()); + Condition cndStore = engGame.getCondition(getString()); + cndStore.duration = (int) parseValue(); + thnStore.addCondition(cndStore); + return true; + } + case 10: { + getLivingThing(getString()).removeCondition(getString()); + return true; + } + case 11: { + LivingThing thnStore = getLivingThing(getString()); + thnStore.flags.add(getString()); + if (vctVisibleUpdate != null || vctVisibleUpdate.contains(thnStore)) { + vctVisibleUpdate.addElement(thnStore); + } + return true; + } + case 12: { + LivingThing thnStore = getLivingThing(getString()); + String strStore2 = getString(); + for (int i = 0; i < thnStore.flags.size(); i++) { + if (strStore2.equalsIgnoreCase((String) thnStore.flags.get(i))) { + return true; + } + } + return false; + } + case 13: { + LivingThing thnStore = getLivingThing(getString()); + String strStore2 = getString(); + for (int i = 0; i < thnStore.flags.size(); i++) { + if (strStore2.equalsIgnoreCase((String) thnStore.flags.get(i))) { + thnStore.flags.remove(i); + if (vctVisibleUpdate != null || vctVisibleUpdate.contains(thnStore)) { + vctVisibleUpdate.addElement(thnStore); + } + return true; + } + } + return true; + } + case 14: { + engGame.varVariables.addVariable(readScript(), getLivingThing(getString())); + engGame.blnVariableListChanged = true; + return true; + } + case 15: { + engGame.varVariables.addVariable(readScript(), parseValue()); + engGame.blnVariableListChanged = true; + return true; + } + case 16: { + engGame.varVariables.addVariable(readScript(), getString()); + engGame.blnVariableListChanged = true; + return true; + } + case 17: { + varVariables.addVariable(readScript(), getLivingThing(getString())); + return true; + } + case 18: { + varVariables.addVariable(readScript(), parseValue()); + return true; + } + case 19: { + varVariables.addVariable(readScript(), getString()); + return true; + } + case 20: { + return (parseScript() | parseScript()); + } + case 21: { + return (parseScript() & parseScript()); + } + case 22: { + return (parseValue() < parseValue()); + } + case 23: { + return (parseValue() > parseValue()); + } + case 24: { + return (parseValue() == parseValue()); + } + case 25: { + if (getLivingThing(getString()).battle != null) { + return true; + } + return false; + } + case 26: { + if (getLivingThing(getString()).clan.equalsIgnoreCase(getString())) { + return true; + } + return false; + } + case 27: { + if (getLivingThing(getString()).race.equalsIgnoreCase(getString())) { + return true; + } + return false; + } + case 28: { + if (getLivingThing(getString()).getItem(getString()) != null) { + return true; + } + return false; + } + case 29: { + Mob mobStore = new Mob(getString(), (int) parseValue(), (int) parseValue(), engGame); + //engGame.vctMobs.add(mobStore); + engGame.addDuskObject(mobStore); + return true; + } + case 30: { + Mob mobStore = new Mob(getString(), (int) parseValue(), (int) parseValue(), engGame); + mobStore.blnOneUse = true; + //engGame.vctMobs.add(mobStore); + engGame.addDuskObject(mobStore); + return true; + } + case 31: { + Item itmStore = engGame.getItem(getString()); + itmStore.x = (int) parseValue(); + itmStore.y = (int) parseValue(); + engGame.addDuskObject(itmStore); + return true; + } + case 59: { + LivingThing thnStore = getLivingThing(getString()); + String strStore2 = getString(); + int i = strStore2.indexOf("."); + int intNum = 1; + if (i != -1) { + try { + intNum = Integer.parseInt(strStore2.substring(0, i)); + strStore2 = strStore2.substring(i + 1, strStore2.length()); + } catch (NumberFormatException e) { + intNum = 1; + } + } + Item itmStore = engGame.getItem(strStore2); + if (itmStore != null) { + // FIXME: put this in addItem() accessor on LivingThing + while (intNum > 0) { + thnStore.itemList.addElement(itmStore); + itmStore.onGetItem(engGame, thnStore); + intNum--; + } + thnStore.updateItems(); + } + return true; + } + case 33: { + LivingThing thnStore = getLivingThing(getString()); + thnStore.getItemAndRemove(getString()); + thnStore.updateItems(); + return true; + } + case 34: { + return getLivingThing(getString()).isWearing(getString()); + } + case 35: { + getLivingThing(getString()).unWear(getString()); + return true; + } + case 36: { + String strStore2 = readScript(); + Variable varStore = null; + if (strStore2.equalsIgnoreCase("global")) { + varStore = engGame.varVariables.getVariable(readScript()); + } else { + varStore = varVariables.getVariable(strStore2); + } + if (varStore != null && varStore.isNumber()) { + return true; + } + return false; + } + case 37: { + String strStore2 = readScript(); + Variable varStore = null; + if (strStore2.equalsIgnoreCase("global")) { + varStore = engGame.varVariables.getVariable(readScript()); + } else { + varStore = varVariables.getVariable(strStore2); + } + if (varStore != null && varStore.isString()) { + return true; + } + return false; + } + case 38: { + String strStore2 = readScript(); + Variable varStore = null; + if (strStore2.equalsIgnoreCase("global")) { + varStore = engGame.varVariables.getVariable(readScript()); + } else { + varStore = varVariables.getVariable(strStore2); + } + if (varStore != null && varStore.isLivingThing()) { + return true; + } + return false; + } + case 39: { + LivingThing thnStore = getLivingThing(getString()); + if (thnStore != null && thnStore.isPlayer()) { + return true; + } + return false; + } + case 40: { + LivingThing thnStore = getLivingThing(getString()); + if (thnStore != null && thnStore.isPet()) { + return true; + } + return false; + } + case 41: { + LivingThing thnStore = getLivingThing(getString()); + if (thnStore != null && thnStore.isMob()) { + return true; + } + return false; + } + case 42: { + LivingThing thnStore = getLivingThing(getString()); + String strStore2 = getString(); + int intIndex = strStore2.indexOf("\n"); + while (intIndex != -1) { + thnStore.chatMessage(strStore2.substring(0, intIndex)); + strStore2 = strStore2.substring(intIndex + 1); + intIndex = strStore2.indexOf("\n"); + } + thnStore.chatMessage(strStore2); + return true; + } + case 43: { + LivingThing thnStore = getLivingThing(getString()); + int red = (int) parseValue(); + int green = (int) parseValue(); + int blue = (int) parseValue(); + String strStore2 = getString(); + int intIndex = strStore2.indexOf("\n"); + while (intIndex != -1) { + thnStore.chatMessage(red, green, blue, strStore2.substring(0, intIndex)); + strStore2 = strStore2.substring(intIndex + 1); + intIndex = strStore2.indexOf("\n"); + } + thnStore.chatMessage(red, green, blue, strStore2); + return true; + } + case 44: { + LivingThing thnStore = getLivingThing(getString()); + bytCommand = rafScript.readByte(); + switch (bytCommand) { + case 0: { + // FIXME: hp damage/adjustment if in battle, should send it through as a message + thnStore.hp += (int) parseValue(); + if (thnStore.hp > (thnStore.maxhp + thnStore.hpbon)) { + thnStore.hp = (thnStore.maxhp + thnStore.hpbon); + } + thnStore.updateInfo(); + return true; + } + case 1: { + thnStore.maxhp += (int) parseValue(); + thnStore.updateInfo(); + return true; + } + case 2: { + thnStore.hpbon += (int) parseValue(); + thnStore.updateInfo(); + return true; + } + case 3: { + thnStore.mp += (int) parseValue(); + if (thnStore.mp > (thnStore.maxmp + thnStore.mpbon)) { + thnStore.mp = (thnStore.maxmp + thnStore.mpbon); + } + thnStore.updateInfo(); + return true; + } + case 4: { + thnStore.maxmp += (int) parseValue(); + thnStore.updateInfo(); + return true; + } + case 5: { + thnStore.mpbon += (int) parseValue(); + thnStore.updateInfo(); + return true; + } + case 6: { + thnStore.damageDone += (int) parseValue(); + return true; + } + case 7: { + thnStore.stre += (int) parseValue(); + if (thnStore.stre > thnStore.stre_limit) { + thnStore.stre = thnStore.stre_limit; + } + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 8: { + thnStore.inte += (int) parseValue(); + if (thnStore.inte > thnStore.inte_limit) { + thnStore.inte = thnStore.inte_limit; + } + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 9: { + thnStore.dext += (int) parseValue(); + if (thnStore.dext > thnStore.dext_limit) { + thnStore.dext = thnStore.dext_limit; + } + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 10: { + thnStore.wisd += (int) parseValue(); + if (thnStore.wisd > thnStore.wisd_limit) { + thnStore.wisd = thnStore.wisd_limit; + } + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 11: { + thnStore.cons += (int) parseValue(); + if (thnStore.cons > thnStore.cons_limit) { + thnStore.cons = thnStore.cons_limit; + } + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 12: { + thnStore.strebon += (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 13: { + thnStore.intebon += (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 14: { + thnStore.dextbon += (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 15: { + thnStore.wisdbon += (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 16: { + thnStore.consbon += (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 17: { + thnStore.acbon += (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 18: { + thnStore.dammodbon += (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 19: { + thnStore.changeLocBypass((int) parseValue() + thnStore.x, thnStore.y); + return true; + } + case 20: { + thnStore.changeLocBypass(thnStore.x, (int) parseValue() + thnStore.y); + return true; + } + case 21: { + thnStore.changeLocBypass((int) parseValue() + thnStore.x, (int) parseValue() + thnStore.y); + return true; + } + case 22: { + thnStore.exp += (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 23: { + thnStore.cash += (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 24: { + String strStore2 = getString(); + int intAddValue = (int) parseValue(); + thnStore.addToSkill(strStore2, intAddValue); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + } + } + case 45: { + + LivingThing thnStore = getLivingThing(getString()); + bytCommand = rafScript.readByte(); + switch (bytCommand) { + case 0: { + thnStore.hp = (int) parseValue(); + if (thnStore.hp > (thnStore.maxhp + thnStore.hpbon)) { + thnStore.hp = (thnStore.maxhp + thnStore.hpbon); + } + thnStore.updateInfo(); + return true; + } + case 1: { + thnStore.maxhp = (int) parseValue(); + thnStore.updateInfo(); + return true; + } + case 2: { + thnStore.hpbon = (int) parseValue(); + thnStore.updateInfo(); + return true; + } + case 3: { + thnStore.mp = (int) parseValue(); + if (thnStore.mp > (thnStore.maxmp + thnStore.mpbon)) { + thnStore.mp = (thnStore.maxmp + thnStore.mpbon); + } + thnStore.updateInfo(); + return true; + } + case 4: { + thnStore.maxmp = (int) parseValue(); + thnStore.updateInfo(); + return true; + } + case 5: { + thnStore.mpbon = (int) parseValue(); + thnStore.updateInfo(); + return true; + } + case 6: { + thnStore.damageDone = (int) parseValue(); + return true; + } + case 7: { + thnStore.stre = (int) parseValue(); + if (thnStore.stre > thnStore.stre_limit) { + thnStore.stre = thnStore.stre_limit; + } + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 8: { + thnStore.inte = (int) parseValue(); + if (thnStore.inte > thnStore.inte_limit) { + thnStore.inte = thnStore.inte_limit; + } + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 9: { + thnStore.dext = (int) parseValue(); + if (thnStore.dext > thnStore.dext_limit) { + thnStore.dext = thnStore.dext_limit; + } + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 10: { + thnStore.wisd = (int) parseValue(); + if (thnStore.wisd > thnStore.wisd_limit) { + thnStore.wisd = thnStore.wisd_limit; + } + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 11: { + thnStore.cons = (int) parseValue(); + if (thnStore.cons > thnStore.cons_limit) { + thnStore.cons = thnStore.cons_limit; + } + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 12: { + thnStore.strebon = (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 13: { + thnStore.intebon = (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 14: { + thnStore.dextbon = (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 15: { + thnStore.wisdbon = (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 16: { + thnStore.consbon = (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 17: { + thnStore.acbon = (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 18: { + thnStore.dammodbon = (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 19: { + thnStore.changeLocBypass((int) parseValue(), thnStore.y); + return true; + } + case 20: { + thnStore.changeLocBypass(thnStore.x, (int) parseValue()); + return true; + } + case 21: { + thnStore.changeLocBypass((int) parseValue(), (int) parseValue()); + return true; + } + case 22: { + thnStore.exp = (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 23: { + thnStore.cash = (int) parseValue(); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + case 24: { + String strStore2 = getString(); + int intNewValue = (int) parseValue(); + thnStore.setSkill(strStore2, intNewValue); + if (vctUpdate != null || vctUpdate.contains(thnStore)) { + vctUpdate.addElement(thnStore); + } + return true; + } + } + } + case 46: { + int dbgX, dbgY; + short dbgValue; + dbgX = (int) parseValue(); + dbgY = (int) parseValue(); + dbgValue = (short) parseValue(); + if (dbgValue == 0) { + engGame.log.printMessage(Log.DEBUG, "Script " + strName + " called changeMap with 0 at " + dbgX + "," + dbgY); + } +// engGame.changeMap((int)parseValue(),(int)parseValue(),(short)parseValue()); + engGame.changeMap(dbgX, dbgY, dbgValue); + return true; + } + case 47: { + String strOne = getString(); + return strOne.equalsIgnoreCase(getString()); + } + case 48: { + String strInput = ""; + LivingThing thnStore = getLivingThing(getString()); + if (!thnStore.isPlayer()) { + engGame.log.printMessage(Log.ERROR, "parseScript():In script " + strName + ", attempt to get input from non-player"); + return false; + } + if (true) + // FIXME: protocol implementation + throw new RuntimeException("cannot ask questions yet"); + // FIXME: this looks dodgy + // FIXME: move to livingthing + thnStore.halt(); + thnStore.stillThere(); // This puts something in the buffer + thnStore.stillThere(); // Need to do this twice to ensure thnStore out of read loop + thnStore.connectionThread.sleep(750); // wait for it... + try { + // Empty out the BufferedReader for the answer + //while (thnStore.instream.ready()) { + // thnStore.instream.read(); + //} + } catch (Exception e) { + engGame.log.printError("parseScript():While trying to empty read buffer to get player input", e); + } + strInput = thnStore.instream.readLine(); + varVariables.addVariable(getString(), strInput); + thnStore.proceed(); + return true; + } + case 49: { + LivingThing thnStore = getLivingThing(getString()); + engGame.notifyRemoved(thnStore); + engGame.addEntity(thnStore); + return true; + } + case 50: { + LivingThing thnStore = getLivingThing(getString()); + String strStore = getString(); + if (thnStore == null) { + return true; + } + strStore = strStore.replace('\n', ' '); + if (thnStore.isPlayer()) { + strStore = "player " + thnStore.name + " " + strStore; + } else if (thnStore.isPet()) { + strStore = "pet " + thnStore.name + " " + strStore; + } else if (thnStore.isMob()) { + strStore = "mob " + thnStore.name + " " + strStore; + } + engGame.log.printMessage(Log.ALWAYS, "ScriptLog:" + strName + ":" + strStore); + return true; + } + case 51: { + String strStore = getString(); + engGame.chatMessage(strStore, "default"); + return true; + } + case 52: { + LivingThing thnStore = getLivingThing(getString()); + String strTitle = getString(); + String strStore2 = getString(); + String strLine = null; + int intIndex = strStore2.indexOf("\n"); + thnStore.send((char) 20 + strTitle + "\n"); + while (intIndex != -1) { + strLine = strStore2.substring(0, intIndex); + thnStore.send(strLine + "\n"); + strStore2 = strStore2.substring(intIndex + 1); + intIndex = strStore2.indexOf("\n"); + } + thnStore.send(strStore2 + "\n"); + thnStore.send("--EOF--\n"); + return true; + } + case 53: { + LivingThing thnStore = getLivingThing(getString()); + if (thnStore == null) { + return true; + } + String strWhat = getString(); + if (strWhat == null) { + return true; + } + if (thnStore.isPlayer()) { + if (engGame.gplosemod != 0) { + int gplost = (int) (thnStore.cash * engGame.gplosemod); + thnStore.chatMessage("You have lost " + gplost + "gp."); + thnStore.cash -= gplost; + } + if (engGame.explosemod != 0) { + int explost = (int) (thnStore.exp * engGame.explosemod); + thnStore.chatMessage("You have lost " + explost + "exp."); + thnStore.exp -= explost; + } + engGame.chatMessage(thnStore.name + " has been killed by " + strWhat, "default"); + if (engGame.scrOnDeath != null && thnStore.isWorking) { + engGame.scrOnDeath.varVariables.clearVariables(); + engGame.scrOnDeath.varVariables.addVariable("trigger", thnStore); + engGame.scrOnDeath.runScript(); + } + } + return true; + } + case 54: { + LivingThing thnStore = getLivingThing("trigger"); + if (thnStore != null && thnStore.isLivingThing()) { + thnStore.isAlwaysCommands = false; + } + return true; + } + case 55: { + LivingThing thnStore = getLivingThing(getString()); + String strClanName = getString(); + if (engGame.isGoodName(strClanName)) { + thnStore.clan = strClanName; + if (thnStore.privs < 1) { + thnStore.privs = 1; + } + } + return true; + } + case 56: { + LivingThing thnStore = getLivingThing(getString()); + String strClanName = getString(); + if (engGame.isGoodName(strClanName)) { + thnStore.clan = strClanName; + if (thnStore.privs == 1) { + thnStore.privs = 0; + } + } + if (vctVisibleUpdate != null || vctVisibleUpdate.contains(thnStore)) { + vctVisibleUpdate.addElement(thnStore); + } + return true; + } + case 57: { + LivingThing thnStore = getLivingThing(getString()); + thnStore.lastMessageStamp = 0; + return true; + } + case 58: { + LivingThing thnStore = getLivingThing(getString()); + String strStore2 = getString(); + int intIndex = strStore2.indexOf("\n"); + while (intIndex != -1) { + if (thnStore.battle != null && thnStore.popup) { + thnStore.send("" + (char) 33 + strStore2.substring(0, intIndex + 1)); + } else { + thnStore.chatMessage(strStore2.substring(0, intIndex)); + } + strStore2 = strStore2.substring(intIndex + 1); + intIndex = strStore2.indexOf("\n"); + } + if (thnStore.battle != null && thnStore.popup) { + thnStore.send("" + (char) 33 + strStore2 + "\n"); + } else { + thnStore.chatMessage(strStore2); + } + return true; + } + case 32: { + return true; + } + /*case 127: + { + return false; + }*/ + } + } catch (Exception e) { + engGame.log.printError("parseScript():While parsing byte(" + bytCommand + ") of script " + strName + " at line " + intLine, e); + return false; + } + engGame.log.printMessage(Log.ERROR, "parseScript():Unknown byte(" + bytCommand + ") parsing script " + strName + " at line " + intLine); + return false; + } + + private String parseValueForCompile() { + try { + String strStore = readScriptForCompile(); + //functions: + if (strStore.equals("+")) { + return ("+ " + parseValueForCompile() + parseValueForCompile()); + } + if (strStore.equals("-")) { + return ("- " + parseValueForCompile() + parseValueForCompile()); + } + if (strStore.equals("*")) { + return ("* " + parseValueForCompile() + parseValueForCompile()); + } + if (strStore.equals("/")) { + return ("/ " + parseValueForCompile() + parseValueForCompile()); + } + if (strStore.equalsIgnoreCase("skill")) { + return "skill " + readScriptForCompile() + " "; + } + return strStore + " "; + } catch (Exception e) { + engGame.log.printError("parseValueForCompile():Compiling script " + strName, e); + } + return null; + } + + private double parseValue() { + try { + String strStore = getString(); + //functions: + if (strStore.equals("+")) { + return (parseValue() + parseValue()); + } + if (strStore.equals("-")) { + return (parseValue() - parseValue()); + } + if (strStore.equals("*")) { + return (parseValue() * parseValue()); + } + if (strStore.equals("/")) { + return (parseValue() / parseValue()); + } + //random number: + if (strStore.equalsIgnoreCase("rand")) { + return Math.random(); + } + //number: + Double dblStore = getNumber(strStore); + if (dblStore != null) { + return dblStore.doubleValue(); + } + //player qualities: + LivingThing target = getLivingThing(strStore); + strStore = getString(); + + if (strStore.equalsIgnoreCase("cp")) { + return (target.getCharacterPoints()); + } + if (strStore.equalsIgnoreCase("tp")) { + return (target.getTotalPoints()); + } + if (strStore.equalsIgnoreCase("cash")) { + return (target.cash); + } + if (strStore.equalsIgnoreCase("exp")) { + return (target.exp); + } + if (strStore.equalsIgnoreCase("locx")) { + return (target.x); + } + if (strStore.equalsIgnoreCase("locy")) { + return (target.y); + } + if (strStore.equalsIgnoreCase("hp")) { + return (target.hp); + } + if (strStore.equalsIgnoreCase("maxhp")) { + return (target.maxhp + target.hpbon); + } + if (strStore.equalsIgnoreCase("mp")) { + return (target.mp); + } + if (strStore.equalsIgnoreCase("maxmp")) { + return (target.maxmp + target.mpbon); + } + if (strStore.equalsIgnoreCase("stre")) { + return (target.stre + target.strebon); + } + if (strStore.equalsIgnoreCase("dext")) { + return (target.dext + target.dextbon); + } + if (strStore.equalsIgnoreCase("inte")) { + return (target.inte + target.intebon); + } + if (strStore.equalsIgnoreCase("wisd")) { + return (target.wisd + target.wisdbon); + } + if (strStore.equalsIgnoreCase("cons")) { + return (target.cons + target.consbon); + } + if (strStore.equalsIgnoreCase("dammod")) { + return target.getDamModWithBonus(); + } + if (strStore.equalsIgnoreCase("ac")) { + return target.getArmorModWithBonus(); + } + if (strStore.equalsIgnoreCase("privs")) { + return target.privs; + } + if (strStore.equalsIgnoreCase("skill")) { + String strStore2 = getString(); + int bytStore = target.getSkill(strStore2); + if (bytStore == 0) { + bytStore = target.getSpell(strStore2); + } + return bytStore; + } + if (strStore.equalsIgnoreCase("count")) { + String strStore2 = getString(); + List list = target.itemList.get(strStore2); + long total = 0; + if (list != null) { + total = list.size(); + } + return total; + } + } catch (Exception e) { + engGame.log.printError("parseValue()", e); + } + return 0; + } +} diff --git a/DuskServer/src/duskz/server/ScriptManager.java b/DuskServer/src/duskz/server/ScriptManager.java new file mode 100644 index 0000000..4e7ec06 --- /dev/null +++ b/DuskServer/src/duskz/server/ScriptManager.java @@ -0,0 +1,300 @@ +/*/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.server; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FilePermission; +import java.io.FileReader; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Permissions; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.security.cert.Certificate; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.ThreadFactory; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.script.Bindings; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.SimpleScriptContext; + +/** + * Manages the (javascript) engine + * + * Doing this securely is complicated, so is likely to be buggy and not really + * secure ... + * + * I found I had to create the script engine itself from the same doPrivileged() + * invocation or it simply broke the sanboxing. Hence all script execution is + * performed from a pool of threads created from the same sandbox. + * + * Another thread monitors the pool checking for out of control scripts (i.e. + * they take too long to run). + * + * @author notzed + */ +public class ScriptManager { + + AccessControlContext ac; + ScriptEngine engine; + LinkedBlockingDeque queue = new LinkedBlockingDeque<>(); + int nthreads = 2; + ExecutorService pool; + static ThreadLocal contexts; + // some sort of shit here for monitoring threads + HashMap active = new HashMap<>(); + + public ScriptManager() { + Permissions perms = new Permissions(); + //perms.add(new AllPermission()); + perms.add(new FilePermission("scripts/*", "read,write")); + // perms.add(new NetPermission("*")); + //perms.add(new RuntimePermission("accessDeclaredMembers")); + // Cast to Certificate[] required because of ambiguity: + ProtectionDomain domain = new ProtectionDomain(new CodeSource(null, (Certificate[]) null), perms); + ac = new AccessControlContext(new ProtectionDomain[]{domain}); + } + + public void start() { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + // If the engine is created here it needs to be used here too. + ScriptEngineManager factory = new ScriptEngineManager(); + engine = factory.getEngineByName("JavaScript"); + contexts = new ThreadLocal<>(); + pool = Executors.newFixedThreadPool(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + + t.setDaemon(true); + return t; + } + }); + + // TOOD: anything else I want to do here? A management/monitoring thread? + + return null; + } + }, ac); + + new WatchThread().start(); + } + + public Future runScript(String script, Object... args) { + return pool.submit(new ScriptData(script, args)); + } + + public Future runScript(File script, Object... args) throws FileNotFoundException { + return pool.submit(new ScriptData(script, args)); + } + + synchronized void startJob(Thread t) { + active.put(t, System.currentTimeMillis()); + } + + synchronized void endJob(Thread t) { + active.remove(t); + } + + synchronized void watchdog() { + // So there is no way to interrupt the executing thread! + // Well it mozilla code after-all ... + + // So i'm forced to use stop with all its apparent dangers. + + long now = System.currentTimeMillis(); + for (Entry e : active.entrySet()) { + if ((now - e.getValue()) > 1000) { + System.out.println("Job taking too long, interrupting it!"); + //e.getKey().interrupt(); + e.getKey().stop(); + } + } + } + + class WatchThread extends Thread { + + public WatchThread() { + setDaemon(true); + } + + @Override + public void run() { + while (true) { + try { + sleep(1000); + + watchdog(); + } catch (InterruptedException ex) { + Logger.getLogger(ScriptManager.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + }; + + class ScriptData implements Callable { + + FileReader scriptReader; + String script; + Map args = new HashMap<>(); + + public ScriptData(String script, Object... args) { + for (int i = 0; i < args.length; i += 2) { + this.args.put((String) args[i], args[i + 1]); + } + this.script = script; + } + + public ScriptData(File scriptFile, Object... args) throws FileNotFoundException { + for (int i = 0; i < args.length; i += 2) { + this.args.put((String) args[i], args[i + 1]); + } + this.scriptReader = new FileReader(scriptFile); + } + + @Override + public T call() throws Exception { + T res = null; + try { + ScriptContext ctx = contexts.get(); + if (ctx == null) { + ctx = new SimpleScriptContext(); + contexts.set(ctx); + } + Bindings b = ctx.getBindings(ScriptContext.ENGINE_SCOPE); + + b.clear(); + b.putAll(args); + + startJob(Thread.currentThread()); + if (scriptReader != null) + res = (T) engine.eval(scriptReader, b); + else + res = (T) engine.eval(script, b); + // lastStart = -1; + b.clear(); + } finally { + endJob(Thread.currentThread()); + if (scriptReader != null) + scriptReader.close(); + } + return res; + } + }; + + public static void main(String[] args) { + // Lets see if it the security shit works + ScriptManager sm = new ScriptManager(); + + sm.start(); + + Future f; + + try { + f = sm.runScript("1;"); + System.out.println("result = " + f.get()); + } catch (Exception ex) { + ex.printStackTrace(System.out); + } + try { + f = sm.runScript("if (true) 0; else 1;"); + System.out.println("result = " + f.get()); + } catch (Exception ex) { + System.out.println(ex); + } + + try { + String s = "var out = new java.io.FileOutputStream('hack-thread.txt');" + + " out.write(65);" + + "out.close();" + + " println('wrote hack.txt ok');"; + + f = sm.runScript(s); + System.out.println("result = " + f.get()); + } catch (Exception ex) { + System.out.println(ex); + } + try { + f = sm.runScript(new File("hack.script")); + System.out.println("result = " + f.get()); + } catch (Exception ex) { + System.out.println(ex); + } + try { + String s = "var out = new java.io.FileInputStream('hack.script');" + + "out.close();" + + " println('read hack.script ok');"; + + f = sm.runScript(s); + System.out.println("result = " + f.get()); + } catch (Exception ex) { + System.out.println(ex); + } + try { + String s = "while (true);"; + f = sm.runScript(s); + System.out.println("result = " + f.get()); + } catch (Exception ex) { + System.out.println(ex); + } + try { + String s = "while (true);"; + f = sm.runScript(s); + System.out.println("result = " + f.get()); + } catch (Exception ex) { + System.out.println(ex); + } + try { + String s = "var out = new java.io.FileOutputStream('scripts/newscript');" + + " out.write(65);" + + "out.close();" + + " println('wrote scripts/newscript ok');"; + + f = sm.runScript(s); + System.out.println("result = " + f.get()); + } catch (Exception ex) { + System.out.println(ex); + } + try { + String s = "while (true);"; + f = sm.runScript(s); + System.out.println("result = " + f.get()); + } catch (Exception ex) { + System.out.println(ex); + } + } +} diff --git a/DuskServer/src/duskz/server/SpellGroup.java b/DuskServer/src/duskz/server/SpellGroup.java new file mode 100644 index 0000000..e853cc3 --- /dev/null +++ b/DuskServer/src/duskz/server/SpellGroup.java @@ -0,0 +1,104 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - modernised collections + */ +package duskz.server; + +import java.util.ArrayList; +import java.util.List; + +/** + * SpellGroup contains all the spells in one group. It provides methods to + * decide how much a spell will cost the player to cast. + * + * @author Tom Weingarten + */ +public class SpellGroup { + + public final String strName; + public final List vctSpells = new ArrayList<>(); + + public SpellGroup(String inName) { + strName = inName; + } + + int getSpellPercent(String strName) { + int i = getSpellNumber(strName); + if (i > 0) { + i = (100 * i) / (vctSpells.size() - 1); + } + return i; + /* + String strStore; + for (int i = 0; i < vctSpells.size(); i++) { + strStore = (String) vctSpells.elementAt(i); + if (strStore.equals(strName)) { + if (i == 0) { + return 0; + } else { + return (100 * i) / (vctSpells.size() - 1); + } + } + } + return -1;*/ + } + + public int getSpellNumber(String strName) { + return vctSpells.indexOf(strName); + /* + String strStore; + for (int i = 0; i < vctSpells.size(); i++) { + strStore = (String) vctSpells.elementAt(i); + if (strStore.equals(strName)) { + return i; + } + } + return -1;*/ + } + + public String spellList(int percent) { + String strStore; + + if (vctSpells.isEmpty()) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + + strStore = (String) vctSpells.get(0); + if (strStore == null) { + return ""; + } + sb.append(" ").append((110 - percent) / 2). + append(" mp) "). + append(strStore).append("\n"); + for (int i = 1; i < vctSpells.size(); i++) { + if (percent < (100 * i) / (vctSpells.size() - 1)) { + break; + } + strStore = (String) vctSpells.get(i); + sb.append(" ").append((110 - (percent - (100 * i) / (vctSpells.size() - 1))) / 2) + .append(" mp) ").append(strStore).append("\n"); + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/DuskServer/src/duskz/server/TickThread.java b/DuskServer/src/duskz/server/TickThread.java new file mode 100644 index 0000000..4c018e6 --- /dev/null +++ b/DuskServer/src/duskz/server/TickThread.java @@ -0,0 +1,104 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - Modernisation, fixed some locking issues + */ +package duskz.server; + +import duskz.server.entity.Mob; +import duskz.server.entity.LivingThing; +import java.util.Iterator; + +public class TickThread implements Runnable { + + DuskEngine game; + long now = System.currentTimeMillis(), + delay = 0; + int tick = 0; + + TickThread(DuskEngine inGame) { + this.game = inGame; + } + + public void run() { + while (true) { + try { + //1000 milliseconds(1 second) per tick + delay = 1000L - (System.currentTimeMillis() - now); + if (delay > 0) { + Thread.currentThread().sleep(delay); + } + now = System.currentTimeMillis(); + + // FIXME: similar code is also in DuskEngine, all unlocked?? + + synchronized (game.mobList) { + for (Mob mob : game.mobList) { + // FIXME: remove magic number!! + if (mob.x == -6) { + mob.hp++; + if (mob.hp > -1) { + mob.changeLocBypass(mob.originalX, mob.originalY); + mob.hp = mob.maxhp; + } + } else { + try { + if (game.blnAI && mob.blnCanSeePlayer && (mob.fctFaction != null)) { + mob.fctFaction.runAI(mob); + } + } catch (Exception e) { + mob.blnCanSeePlayer = false; + game.log.printError("TickThread.run():While trying to runAI for mob \"" + mob.name + "\".", e); + } + mob.moveTick(); + } + } + } + for (LivingThing lt : game.checkConditionList) { + for (Iterator it = lt.conditions.iterator(); it.hasNext();) { + Condition cond = it.next(); + if (cond.ticksPast >= cond.occurance) { + cond.onOccurance(game, lt); + if (cond.duration > 0) { + cond.duration--; + } + if (cond.duration == 0) { + cond.onEnd(game, lt); + it.remove(); + } + cond.ticksPast = 1; + } else { + cond.ticksPast++; + } + } + } + + if (tick > 120) { + tick = 0; + } + tick++; + } catch (Exception e) { + game.log.printError("ThreadTicks.run()", e); + } + } + } +} \ No newline at end of file diff --git a/DuskServer/src/duskz/server/TrackerThread.java b/DuskServer/src/duskz/server/TrackerThread.java new file mode 100644 index 0000000..d2cba7e --- /dev/null +++ b/DuskServer/src/duskz/server/TrackerThread.java @@ -0,0 +1,148 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.server; + +import duskz.server.entity.LivingThing; +import java.net.DatagramSocket; +import java.net.DatagramPacket; +import java.net.InetAddress; + +public class TrackerThread implements Runnable { + + DuskEngine engGame; + DatagramSocket sckTracker; + + TrackerThread(DuskEngine inGame) { + engGame = inGame; + } + + void initializeTracker() { + if (engGame.tracker && sckTracker == null) { + try { + engGame.log.printMessage(Log.ALWAYS, "Communicating with tracker"); + sckTracker = new DatagramSocket(engGame.trackerport); + sckTracker.setSoTimeout(0); //no timeout + String s1 = "\000" + engGame.trackername; + DatagramPacket datagrampacket = new DatagramPacket(s1.getBytes(), s1.length()); + datagrampacket.setAddress(InetAddress.getByName("dusk.wesowin.org")); + datagrampacket.setPort(7520); + sckTracker.send(datagrampacket); + s1 = "\002" + engGame.port; + datagrampacket.setData(s1.getBytes()); + datagrampacket.setLength(s1.length()); + sckTracker.send(datagrampacket); + if (!engGame.site.equals("none")) { + String s2 = "\003" + engGame.site; + datagrampacket.setData(s2.getBytes()); + datagrampacket.setLength(s2.length()); + sckTracker.send(datagrampacket); + } + engGame.tracker = true; + } catch (Exception e) { + engGame.log.printError("initializeTracker():Failed to contact tracker, disabling", e); + sckTracker = null; + engGame.tracker = false; + } + } + } + + void updateTrackerUsers() { + if (!engGame.tracker) { + return; + } + + try { + int count = 0; + for (LivingThing thnStore : engGame.playersByName.values()) { + if (!(thnStore.privs > 2 && thnStore.hasCondition("invis"))) { + count++; + } + } + String s = "\001" + count; + DatagramPacket datagrampacket = new DatagramPacket(s.getBytes(), s.length()); + datagrampacket.setAddress(InetAddress.getByName("dusk.wesowin.org")); + datagrampacket.setPort(7520); + sckTracker.send(datagrampacket); + } catch (Exception e) { + engGame.log.printError("updateTrackerUsers()", e); + } + } + + void pingTracker() { + if (!engGame.tracker) { + return; + } + + DatagramPacket pckStore = new DatagramPacket(new byte[1], 1); + + try { + pckStore.setAddress(InetAddress.getByName("dusk.wesowin.org")); + pckStore.setPort(7520); + pckStore.setData("\005".getBytes()); + pckStore.setLength(1); + sckTracker.send(pckStore); + } catch (Exception e) { + engGame.log.printError("pingTracker():While communicating with tracker", e); + sckTracker = null; + engGame.tracker = false; + } + } + + void removeFromTracker() { + if (!engGame.tracker) { + return; + } + + try { + String s = "\004"; + DatagramPacket datagrampacket = new DatagramPacket(s.getBytes(), s.length()); + datagrampacket.setAddress(InetAddress.getByName("dusk.wesowin.org")); + datagrampacket.setPort(7520); + sckTracker.send(datagrampacket); + } catch (Exception e) { + engGame.log.printError("removeFromTracker()", e); + } + } + + public void run() { + if (!engGame.tracker) { + return; + } + + engGame.log.printMessage(Log.ALWAYS, "Starting tracker thread"); + + initializeTracker(); + + while (engGame.tracker) { + try { + Thread.currentThread().sleep(300000); + } catch (Exception e) { + engGame.log.printError("TrackerThread.run()", e); + } + updateTrackerUsers(); + pingTracker(); + } + + removeFromTracker(); + } +} \ No newline at end of file diff --git a/DuskServer/src/duskz/server/Variable.java b/DuskServer/src/duskz/server/Variable.java new file mode 100644 index 0000000..8b448cf --- /dev/null +++ b/DuskServer/src/duskz/server/Variable.java @@ -0,0 +1,88 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - separated out from VariableSet for public access + */ +package duskz.server; + +/** + * Holder for variables. + * + * TODO: make this OO. Will need fixing for javascript. + * + * @author notzed + */ +public class Variable { + + String strName; + Object objData; + byte bytType; + public final static byte NUMBER = 0; + public final static byte STRING = 1; + public final static byte THING = 2; + + public Variable(String strName, Object objData, byte bytType) { + this.strName = strName; + this.objData = objData; + this.bytType = bytType; + } + + public double doubleValue() { + return (Double) objData; + } + + public int intValue() { + return ((Number) objData).intValue(); + } + + //0-Number + //1-String + //2-LivingThing + //3-Item + boolean isNumber() { + if (bytType == 0) { + return true; + } + return false; + } + + boolean isString() { + if (bytType == 1) { + return true; + } + return false; + } + + boolean isLivingThing() { + if (bytType == 2) { + return true; + } + return false; + } + + boolean isItem() { + if (bytType == 3) { + return true; + } + return false; + } +} diff --git a/DuskServer/src/duskz/server/VariableSet.java b/DuskServer/src/duskz/server/VariableSet.java new file mode 100644 index 0000000..4849471 --- /dev/null +++ b/DuskServer/src/duskz/server/VariableSet.java @@ -0,0 +1,107 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - modernised java, separated out Variable. + */ +package duskz.server; + +import duskz.server.entity.LivingThing; +import java.util.HashMap; + +/** + * Contains a set of variables for use in a DuskScript. Variables may be of type + * number, livingthing, or string. + * + * @author Tom Weingarten + */ +public class VariableSet { + + //Vector vctVariables; + public final HashMap vctVariables = new HashMap<>(); + + public VariableSet() { + } + + public void clearVariables() { + vctVariables.clear(); + } + + protected void addVariable(Variable v) { + String n = v.strName.toLowerCase(); + if (n.equals("name") + || n.equals("clan") + || n.equals("concat")) { +// engGame.log.printMessage(Log.ERROR, "addVariable():You cannot create a variable with the name \""+strName+"\"."); + return; + } + vctVariables.put(n, v); + } + + public void addVariable(String strName, Double objIn) { + addVariable(new Variable(strName, objIn, Variable.NUMBER)); + } + + public void addVariable(String strName, double objIn) { + addVariable(new Variable(strName, objIn, Variable.NUMBER)); + } + + public void addVariable(String strName, String objIn) { + addVariable(new Variable(strName, objIn, Variable.STRING)); + } + + public void addVariable(String strName, LivingThing objIn) { + addVariable(new Variable(strName, objIn, Variable.THING)); + } + + /*void addVariable(String strName, Item objIn) + { + if (strName.equalsIgnoreCase("name") + ||strName.equalsIgnoreCase("clan") + ||strName.equalsIgnoreCase("concat")) + { + // engGame.log.printMessage(Log.ERROR, "addVariable():You cannot create a variable with the name \""+strName+"\"."); + return; + } + Variable varStore; + for (int i=0;i + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.server.entity; + +/** + * Base class for trained attributes. + * + * Training goes up to 100. + * + * FIXME: there's something major wrong with abilities!! + * + * @author notzed + */ +public class Ability { + + private int ability; + public final String name; + + public Ability(String name) { + this.name = name; + } + + public Ability(int ability, String name) { + ability = Math.max(ability, 0); + ability = Math.min(ability, 100); + this.ability = ability; + this.name = name; + } + + public void setAbility(int ability) { + ability = Math.max(ability, 0); + ability = Math.min(ability, 100); + this.ability = ability; + } + + /** + * Train an ability. + * + * @param delta + * @return true if the ability was increased, false if it is already at maximum + */ + public boolean train(int delta) { + if (ability < 100) { + setAbility(ability + delta); + return true; + } + return false; + } + + public int getAbility() { + return ability; + } +} diff --git a/DuskServer/src/duskz/server/entity/DuskObject.java b/DuskServer/src/duskz/server/entity/DuskObject.java new file mode 100644 index 0000000..fb48dc3 --- /dev/null +++ b/DuskServer/src/duskz/server/entity/DuskObject.java @@ -0,0 +1,242 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - modernised java, added list functions and + * serialisation.. + */ +package duskz.server.entity; + +import duskz.protocol.MessageType; +import duskz.protocol.ServerMessage; +import duskz.protocol.ServerMessage.EntityMessage; + +/* + All code copyright Tom Weingarten (captaint@home.com) 2000 + Tom Weingarten makes no assurances as to the reliability or + functionality of this code. Use at your own risk. + + You are free to edit or redistribute this code or any portion + at your wish, under the condition that you do not edit or + remove this license, and accompany it with all redistributions. + */ +/** + * DuskObject is the basic type of entity in Dusk. + * + * @author Tom Weingarten + */ +public abstract class DuskObject { + + public static final byte LIVING_THING = 0; + public static final byte ITEM = 1; + public static final byte PROP = 2; + public static final byte SIGN = 3; + public static final byte MERCHANT = 4; + public static final byte PLAYER_MARCHANT = 5; + final public long ID; // Unique ID if this object + public String name; // Name of this object + public String description = null; // Description of this object + boolean isHideName; // if true: Do not display object's name on the client map. + public int x, y; + DuskObject next = null; //Linked List + + public DuskObject(long ID, String name) { + this.ID = ID; + this.name = name; + } + + public DuskObject(byte bytObjType, long ID) { + this.ID = ID; + } + + public abstract byte getType(); + + public abstract int getImage(); + // for some fucked reason this doesn't match getType() + + public abstract int getEntityType(); + + // These are a bit dumb - we have objects to change behaviour. + public boolean isLivingThing() { + return getType() == LIVING_THING; + } + + public boolean isItem() { + return getType() == ITEM; + } + + public boolean isProp() { + return getType() == PROP; + } + + public boolean isSign() { + return getType() == SIGN; + } + + public boolean isMerchant() { + return getType() == MERCHANT; + } + + public boolean isPlayerMerchant() { + return getType() == PLAYER_MARCHANT; + } + + public long getID() { + return ID; + } + + /** + * Convert to on-wire entity format + * + * @param eng + * @return + */ + public String toEntity() { + StringBuilder sb = new StringBuilder(); + + sb.append(name).append('\n'); + sb.append(getEntityType()).append('\n'); + sb.append(ID).append('\n'); + sb.append(x).append('\n'); + sb.append(y).append('\n'); + sb.append(getImage()).append('\n'); + + return sb.toString(); + } + + public EntityMessage toMessage(MessageType type) { + return new ServerMessage.EntityMessage(type, ID, name, + (byte) getEntityType(), (short) x, (short) y, (short) getImage(), (short) -1); + } + + // Linked list stuff - should it just use a container? + public DuskObject getNext() { + return next; + } + + /** + * Measure the distance between two objects. + * + * @param o Other object + * @return integer version of Euclidian distance. + */ + public int distance(DuskObject o) { + int dx = o.x - x; + int dy = o.y - y; + + return (int) Math.sqrt(dx * dx + dy * dy); + } + + /** + * Measure the number of tile steps to get to the target. Just uses + * "Manhatten" distance. + * + * @param o + * @return + */ + public int tileDistance(DuskObject o) { + return Math.abs(o.x - x) + Math.abs(o.y - y); + } + + /** + * Calculate if this object is 1 block away in any cardinal direction + * + * @param o + * @return + */ + public boolean adjacent(DuskObject o) { + return Math.abs(o.x - x) + Math.abs(o.y - y) <= 1; + } + + @Override + public String toString() { + return getClass().getName() + " '" + name + "' @ " + x + "," + y; + } + + @Deprecated + public static DuskObject find(DuskObject o, String name) { + while (o != null) { + if (o.name.equalsIgnoreCase(name)) { + break; + } + o = o.next; + } + return o; + } + + /** + * append o to the end of the bucket chain from root + * + * @param root + * @param o + * @return if root is null then o, otherwise root + */ + public static DuskObject append(DuskObject root, DuskObject o) { + if (o == root) { + return root; + } + + if (root == null) { + o.next = null; + return o; + } + + DuskObject d = root; + while (d.next != null) { + if (d == o) { + return root; + } + d = d.next; + } + o.next = null; + d.next = o; + + return root; + } + + /** + * Remove o from a chain from root, returns new root + * + * @param root + * @param o + * @return + */ + public static DuskObject remove(DuskObject root, DuskObject o) { + if (root == null) { + return null; + } else if (root == o) { + DuskObject n = o.next; + o.next = null; + return n; + } else { + DuskObject d = root; + while (d.next != null) { + if (d.next == o) { + d.next = o.next; + o.next = null; + break; + } + d = d.next; + } + return root; + } + } +} diff --git a/DuskServer/src/duskz/server/entity/Equipment.java b/DuskServer/src/duskz/server/entity/Equipment.java new file mode 100644 index 0000000..5eb7d78 --- /dev/null +++ b/DuskServer/src/duskz/server/entity/Equipment.java @@ -0,0 +1,191 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - modernised java, big refactor. + */ +package duskz.server.entity; + +import duskz.protocol.Wearing; + +/** + * Equipment contains all the Items a LivingThing is wearing. + * + * @author Tom Weingarten + */ +public class Equipment implements Wearing { + + // Must match Wearing + public static final String[] USER_NAMES = { + "wield2", "arms2", "legs2", "torso2", "waist2", "neck2", "skull2", "eyes2", "hands2" + }; + public static final float[] ARMOUR_MOD = { + 0f, 0.05f, 0.05f, 0.40f, 0.15f, 0.05f, 0.20f, 0.05f, 0.05f + }; + private Item[] worn = new Item[WEARING_COUNT]; + + /** + * Encode for network. + * + * @return + */ + public String toEntity() { + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < worn.length; i++) { + Item item = worn[i]; + if (item != null) + sb.append(item.name).append('\n'); + else + sb.append("none\n"); + } + return sb.toString(); + } + + public Item getWorn(int where) { + return worn[where]; + } + + public boolean isWearing(String what) { + for (Item item : worn) { + if (item != null && what.equalsIgnoreCase(item.name)) + return true; + } + return false; + } + + /** + * Where a sepecific item + * + * @param where + * @param item + * @return + */ + public Item wear(int where, Item item) { + Item old = worn[where]; + + worn[where] = item; + return old; + } + + /** + * Unwear item at specific location + * + * @param where + * @return item if worn + */ + public Item unwear(int where) { + Item old = worn[where]; + + worn[where] = null; + return old; + } + + /** + * Unwear an item of a sepcific name + * + * @param name + * @return item if worn + */ + public Item unwearByName(String name) { + for (int i = 0; i < worn.length; i++) { + Item item = worn[i]; + + if (item != null && name.equalsIgnoreCase(item.name)) { + worn[i] = null; + return item; + } + } + return null; + } + + /** + * Convert to wield type index + * + * @param where + * @return + */ + public static int toIndex(String where) { + switch (where.toLowerCase()) { + case "wielded": + return WIELD; + case "arms": + return ARMS; + case "legs": + return LEGS; + case "torso": + return TORSO; + case "waist": + return WAIST; + case "neck": + return NECK; + case "skull": + return SKULL; + case "eyes": + return EYES; + case "hands": + return HANDS; + } + return -1; + } + + public int armourCount() { + int count = 0; + for (int i = ARMS; i < worn.length; i++) { + if (worn[i] != null) + count++; + } + return count; + } + + public int armourMod() { + int mod = 0; + + for (int i = 0; i < worn.length; i++) { + Item item = worn[i]; + + if (item != null) { + mod += ARMOUR_MOD[i] * item.intMod; + } + } + return mod; + } + + /** + * Damage the specified item if it exists + * + * @param damage + * @return the weapon if it was destroyed + */ + public Item damageItem(int index, int damage) { + Item item = worn[index]; + + if (item != null && item.lngDurability != -1) { + item.lngDurability -= damage; + if (item.lngDurability < 0) { + worn[index] = null; + return item; + } + } + return null; + } + // TODO: load/save functions here too? +} diff --git a/DuskServer/src/duskz/server/entity/Item.java b/DuskServer/src/duskz/server/entity/Item.java new file mode 100644 index 0000000..b48901e --- /dev/null +++ b/DuskServer/src/duskz/server/entity/Item.java @@ -0,0 +1,291 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - modernised java, factory method/loader and script + * wrappers. + */ +package duskz.server.entity; + +import duskz.server.DuskEngine; +import duskz.server.Script; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; + +/* + All code copyright Tom Weingarten (captaint@home.com) 2000 + Tom Weingarten makes no assurances as to the reliability or + functionality of this code. Use at your own risk. + + You are free to edit or redistribute this code or any portion + at your wish, under the condition that you do not edit or + remove this license, and accompany it with all redistributions. + */ +//0-Ordinary Item +//1-Weapon +//2-Armor +//3-Food +//4-Drink +/** + * An Item is an object that can be picked up, worn, and used. It can be of type + * Ordinary Item, Weapon, Armor, Food, or Drink. + * + * @author Tom Weingarten + */ +public class Item extends DuskObject { + + public static final int ORDINARY = 0; + public static final int WEAPON = 1; + public static final int ARMOR = 2; + public static final int FOOD = 3; + public static final int DRINK = 4; + public static final int CONTAINER = 5; + private String strOnGetScript = null, + strOnDropScript = null; + public String strOnUseScript = null, + strOnWearScript = null, + strOnUnWearScript = null, + strOnOpenScript = null, + strOnCloseScript = null, + strOnAddItemScript = null, + strOnRemoveItemScript = null; + public int intCost, + intType = -1, + intKind = -1, + intMod, + intUses = -1, + intRange = 1; + int intImage; + int intWeight; // How much this object weighs + int intSize; // Amount of space this object takes up + int intCapacity; // How many items can this container hold + int intWeightLimit; // How much weight can this container hold + public long lngDurability = -1; + Item itmNext = null; + + public Item(long lngID, String name) { + super(lngID, name); + } + + boolean isOrdinaryItem() { + if (intType == ORDINARY) { + return true; + } + return false; + } + + boolean isWeapon() { + if (intType == WEAPON) { + return true; + } + return false; + } + + boolean isArmor() { + if (intType == ARMOR) { + return true; + } + return false; + } + + boolean isFood() { + if (intType == FOOD) { + return true; + } + return false; + } + + boolean isDrink() { + if (intType == DRINK) { + return true; + } + return false; + } + + boolean isContainer() { + if (intType == CONTAINER) { + return true; + } + return false; + } + + int range() { + return intRange; + } + + @Override + public int getImage() { + return intImage; + } + + @Override + public byte getType() { + return ITEM; + } + + @Override + public int getEntityType() { + return 1; + } + + /* + ** This method formats the Item for saving. + ** It generates a String that can later be passed + ** to fromString for populating a new Item. + */ + public String toString() { + return "[strName:" + name + + "::strDescription:" + description + + "::strOnUseScript:" + strOnUseScript + + "::strOnWearScript:" + strOnWearScript + + "::strOnUnWearScript:" + strOnUnWearScript + + "::strOnGetScript:" + strOnGetScript + + "::strOnDropScript:" + strOnDropScript + + "::intCost:" + intCost + + "::intType:" + intType + + "::intKind:" + intKind + + "::intMod:" + intMod + + "::intImage:" + intImage + + "::intUses:" + intUses + + "::intRange:" + intRange + + "::lngDurability:" + lngDurability + + "]"; + } + + public static Item getItem(long id, String inName) throws IOException { + Item itmStore = null; + RandomAccessFile rafItemDef = null; + String strStore = "Error reading file"; + try { + File fileTest = new File("defItems/" + inName.toLowerCase()); + if (!fileTest.exists()) { + return null; + } + rafItemDef = new RandomAccessFile("defItems/" + inName.toLowerCase(), "r"); + //itmStore = new Item(this); + itmStore = new Item(id, inName); + itmStore.description = "a " + inName; //default description is name + strStore = rafItemDef.readLine(); + while (!(strStore == null || strStore.equals("."))) { + // FIXME: use enums + if (strStore.equalsIgnoreCase("type")) { + strStore = rafItemDef.readLine(); + if (strStore.equalsIgnoreCase("item")) { + itmStore.intType = 0; + } else if (strStore.equalsIgnoreCase("weapon")) { + itmStore.intType = 1; + } else if (strStore.equalsIgnoreCase("armor")) { + itmStore.intType = 2; + } else if (strStore.equalsIgnoreCase("food")) { + itmStore.intType = 3; + } else if (strStore.equalsIgnoreCase("drink")) { + itmStore.intType = 4; + } + } else if (strStore.equalsIgnoreCase("kind")) { + strStore = rafItemDef.readLine(); + if (strStore.equalsIgnoreCase("arms")) { + itmStore.intKind = 0; + } else if (strStore.equalsIgnoreCase("legs")) { + itmStore.intKind = 1; + } else if (strStore.equalsIgnoreCase("torso")) { + itmStore.intKind = 2; + } else if (strStore.equalsIgnoreCase("waist")) { + itmStore.intKind = 3; + } else if (strStore.equalsIgnoreCase("neck")) { + itmStore.intKind = 4; + } else if (strStore.equalsIgnoreCase("skull")) { + itmStore.intKind = 5; + } else if (strStore.equalsIgnoreCase("eyes")) { + itmStore.intKind = 6; + } else if (strStore.equalsIgnoreCase("hands")) { + itmStore.intKind = 7; + } + } else if (strStore.equalsIgnoreCase("description")) { + itmStore.description = rafItemDef.readLine(); + } else if (strStore.equalsIgnoreCase("cost")) { + itmStore.intCost = Integer.parseInt(rafItemDef.readLine()); + } else if (strStore.equalsIgnoreCase("durability")) { + itmStore.lngDurability = Long.parseLong(rafItemDef.readLine()); + } else if (strStore.equalsIgnoreCase("uses")) { + itmStore.intUses = Integer.parseInt(rafItemDef.readLine()); + } else if (strStore.equalsIgnoreCase("mod")) { + itmStore.intMod = Integer.parseInt(rafItemDef.readLine()); + } else if (strStore.equalsIgnoreCase("range")) { + itmStore.intRange = Integer.parseInt(rafItemDef.readLine()); + } else if (strStore.equalsIgnoreCase("image")) { + itmStore.intImage = Integer.parseInt(rafItemDef.readLine()); + } else if (strStore.equalsIgnoreCase("onuse")) { + itmStore.strOnUseScript = rafItemDef.readLine(); + } else if (strStore.equalsIgnoreCase("onwear")) { + itmStore.strOnWearScript = rafItemDef.readLine(); + } else if (strStore.equalsIgnoreCase("onunwear")) { + itmStore.strOnUnWearScript = rafItemDef.readLine(); + } else if (strStore.equalsIgnoreCase("onget")) { + itmStore.strOnGetScript = rafItemDef.readLine(); + } else if (strStore.equalsIgnoreCase("ondrop")) { + itmStore.strOnDropScript = rafItemDef.readLine(); + } + strStore = rafItemDef.readLine(); + } + } catch (NumberFormatException ex) { + throw new IOException("Parsing: " + strStore, ex); + } catch (NullPointerException ex) { + throw new IOException(ex); + } finally { + if (rafItemDef != null) { + rafItemDef.close(); + } + } + return itmStore; + } + + public void onGetItem(DuskEngine eng, LivingThing trigger) { + if (strOnGetScript != null) { + try { + Script script = new Script("scripts/" + strOnGetScript, eng, false); + script.varVariables.addVariable("trigger", trigger); + script.varVariables.addVariable("itemname", name); + script.varVariables.addVariable("mod", intMod); + script.runScript(); + script.close(); + } catch (Exception e) { + eng.log.printError("While running onGet script(" + strOnGetScript + ") for item \"" + name + "\"", e); + } + } + } + + public void onDropItem(DuskEngine eng, LivingThing trigger) { + if (strOnDropScript != null) { + try { + Script script = new Script("scripts/" + strOnDropScript, eng, false); + script.varVariables.clearVariables(); + script.varVariables.addVariable("trigger", trigger); + script.varVariables.addVariable("itemname", name); + script.varVariables.addVariable("mod", intMod); + script.runScript(); + script.close(); + } catch (Exception e) { + eng.log.printError("While running onDrop script(" + strOnDropScript + ") for item \"" + name + "\"", e); + } + } + } +} diff --git a/DuskServer/src/duskz/server/entity/LivingThing.java b/DuskServer/src/duskz/server/entity/LivingThing.java new file mode 100644 index 0000000..4c5e9f6 --- /dev/null +++ b/DuskServer/src/duskz/server/entity/LivingThing.java @@ -0,0 +1,2914 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - modernised java, lots of clean up and encapsulation + * and synchronisation fixes. Moved loader here. + */ +package duskz.server.entity; + +import duskz.server.Battle; +import duskz.server.Commands; +import duskz.server.Condition; +import duskz.server.DuskEngine; +import duskz.server.ItemList; +import duskz.server.Log; +import duskz.protocol.MessageType; +import duskz.server.Script; +import duskz.server.SpellGroup; +import duskz.server.Variable; +import duskz.server.entity.TileMap.MapData; +import duskz.server.entity.TileMap.MoveData; +import duskz.protocol.ServerMessage; +import java.io.*; +import java.net.Socket; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * LivingThing defines the base class for all moving objects. This includes + * Players, Pets, and Mobs. + * + * @author Tom Weingarten + */ +public class LivingThing extends DuskObject implements Runnable { + + DuskEngine game; + final public LinkedBlockingQueue messageQueue = new LinkedBlockingQueue<>(); + //StillWorking? + public boolean isWorking = true; + //Has the player finished loading yet? + public boolean isLoaded = false; + //Should the player be in who yet? + public boolean isReady = false; + //Can we save the player yet? + public boolean isSaveable = false; + //SHOULD we save the player yet? + public boolean isSaveNeeded = false; + //Is player in process of closing? + public boolean isClosing = false; + //Should we stop the thread? + public boolean isStopped = false; + //Should we process in-game commands after script is finished? + //(The script can change this to false to stop it from happening) + //(This allows scripts to short-circuit in-game commands) + public boolean isAlwaysCommands = true; + // File names for i/o + File file, backup; + //ID data + public String password, + clan = "none", + title = null, + masterName = null; + //Group + LivingThing following = null, + master = null, + charmer = null; + //Stats + public int playerID, // uniquely identifies player for land ownership + hp, + maxhp, + mp, + maxmp, + originalX, + originalY, + stre, + inte, + dext, + cons, + wisd, + exp, + hpbon = 0, + mpbon = 0, + strebon = 0, + intebon = 0, + dextbon = 0, + consbon = 0, + wisdbon = 0, + dammodbon = 0, + rangebon = 0, + acbon = 0; + //Max values for stats + public int hp_limit = -1, + mp_limit = -1, + exp_limit = -1, + stre_limit = 100, + inte_limit = 100, + dext_limit = 100, + cons_limit = 100, + wisd_limit = 100; + // FIXME: not public + public long cash; + // FIXME: not public + public long lastMessageStamp = 0; + //Status + // FIXME: accessor + public boolean isSleeping; + //Is this livingthing currently allowed to move? + public boolean isMoveable = true; + //For commands that need input... + boolean isHalted = false; + //Battle + // FIXME: accessor + public Battle battle = null; + public byte battleSide; + public long damageDone; + //Flags + // FIXME: accessor/helper + final public List flags = new LinkedList<>(); + //Conditions + final public List conditions = new ArrayList<>(); + //Skills + final HashMap skillMap = new HashMap<>(2); + //Spells + final HashMap spellMap = new HashMap<>(2); + // worn, FIXME: not public + public Equipment wornItems; + //Items + public ItemList itemList; + //Movement queue + final private LinkedList moveQueue = new LinkedList<>(); + //Entities (for optimized refresh) + private final HashMap nearEntities = new HashMap<>(); + //Image id + int imageid; + // Image step for animation selection + public int imagestep; + //Race + public String race = null; + //Priveleges + public int privs = 0; + //Socket connection + private Socket socket; + // FIXME: not public + public DataInputStream instream; + // FIXME: not public + public DataOutputStream outstream; + public Thread connectionThread; + private SendThread sendThread; + //Prefs + public boolean audioon = true, + coloron = true, + popup = true, + highlight = true, + noFollow = false; + public int noChannel = 0; + //Timestamp of last save + long lastSaveStamp; + //List of players being ignored + final public List ignoreList = new ArrayList<>(); + //Type + /* + 0=Player + 1=Mob + 2=Pet + */ + byte Type; + + public boolean isPlayer() { + if (Type == 0) { + return true; + } + return false; + } + + public boolean isMob() { + if (Type == 1) { + return true; + } + return false; + } + + public boolean isPet() { + if (Type == 2) { + return true; + } + return false; + } + + @Override + public byte getType() { + return LIVING_THING; + } + + @Override + public int getImage() { + return imageid; + } + + @Override + public int getEntityType() { + if (isMob() || isPet()) { + return 4; + } + return 0; + } + + /** + * Get IP address of user + * + * @return + */ + public String getAddress() { + if (socket != null + && socket.isConnected()) + return socket.getInetAddress().toString(); + else + return null; + } + + @Override + public String toEntity() { + StringBuilder sb = new StringBuilder(); + if (isSleeping) { + sb.append(""); + } + if (isPlayer() && !clan.equals("none")) { + sb.append('<'); + sb.append(clan); + sb.append('>'); + } + if (isPet() && hp < 0) { + sb.append(""); + } + for (String s : flags) { + sb.append('<'); + sb.append(s); + sb.append('>'); + } + sb.append(name).append('\n'); + sb.append(getEntityType()).append('\n'); + sb.append(ID).append('\n'); + sb.append(x).append('\n'); + sb.append(y).append('\n'); + sb.append(getImage()).append('\n'); + if (isPlayer()) { + sb.append(imagestep).append('\n'); + } + return sb.toString(); + } + + @Override + public ServerMessage.EntityMessage toMessage(MessageType type) { + StringBuilder sb = new StringBuilder(); + if (isSleeping) { + sb.append(""); + } + if (isPlayer() && !clan.equals("none")) { + sb.append('<'); + sb.append(clan); + sb.append('>'); + } + if (isPet() && hp < 0) { + sb.append(""); + } + for (String s : flags) { + sb.append('<'); + sb.append(s); + sb.append('>'); + } + sb.append(name); + return new ServerMessage.EntityMessage(type, ID, sb.toString(), + (byte) getEntityType(), (short) x, (short) y, (short) getImage(), (short) (isPlayer() ? imagestep : -1)); + } + + /** + * for extended classes + */ + public LivingThing(long ID, String name) { + super(ID, name); + } + + /** + * new Player + */ + public LivingThing(Socket inSocket, DuskEngine inEngine) { + super(LIVING_THING, inEngine.getID()); + try { + sendThread = new SendThread(); + sendThread.start(); + Type = 0; + game = inEngine; + socket = inSocket; + socket.setSoTimeout(600000); //10 minute timeout + socket.setSoLinger(false, 0); //Do not linger on disconnect + instream = new DataInputStream(new BufferedInputStream(socket.getInputStream())); + outstream = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())); + game.log.printMessage(Log.INFO, socket.toString()); + battle = null; + isSleeping = false; + + // + // WTF is this stuff doing here?? + + if (game.maxconnections != 0 && game.playersByName.size() >= game.maxconnections) { + chatMessage("Sorry, the server has reached it's connection limit. Try again later."); + send("Goodbye.\n" + (char) 0); + Thread.sleep(1000); + closeNosavePlayer(); + return; + } + // Game is shutting down + if (game.blnShuttingDown) { + chatMessage("Sorry, the server is not accepting new connections. It is either being shutdown or worked on. Try again later."); + send("Goodbye.\n" + (char) 0); + Thread.sleep(1000); + closeNosavePlayer(); + return; + } + if (!game.isGoodIP(socket.getInetAddress().toString())) { + chatMessage("Connections from your machine are no longer being accepted."); + send("Goodbye.\n" + (char) 0); + Thread.sleep(1000); + closeNosavePlayer(); + return; + } + // IP Filter FIXME: put on engine + if (game.blnIPF) { + for (LivingThing lt : game.playersByName.values()) { + String IP = lt.socket.getInetAddress().toString(); + if (IP.equalsIgnoreCase(socket.getInetAddress().toString())) { + chatMessage("There's already a player connected from your IP address."); + send("Goodbye.\n" + (char) 0); + Thread.sleep(1000); + closeNosavePlayer(); + return; + } + } + } + // End IPF + } catch (Exception e) { + game.log.printError("LivingThing():Creating player", e); + } + } + + /** + * new Pet + */ + public LivingThing(String strName, String strType, LivingThing master, DuskEngine inEngine) { + super(inEngine.getID(), strName); + try { + Type = 2; + game = inEngine; + this.master = master; + masterName = this.master.name.toLowerCase(); + charmer = this.master; + x = this.master.x; + y = this.master.y; + battle = null; + isSleeping = false; + wornItems = new Equipment(); + itemList = new ItemList(); + + //Load pet files + + String strStore = ""; + if (strType != null) { + race = strType; + } + /* + ** check to see if a pet exists + */ + File filCheck; + if (strName.equalsIgnoreCase("default")) { + strStore = this.master.name.toLowerCase(); + filCheck = new File("pets/" + strStore); + if (!filCheck.exists() && strType == null) { + closeNosavePlayer(); + return; + } + } + loadUserFile(new File("pets/default")); + strStore = this.master.name.toLowerCase(); + File filPlayer = new File("pets/" + strStore); + File filBackup = new File("pets/" + strStore + ".backup"); + /* + ** Check for old style pet name + ** (Only need to check if we already have a valid pet name) + */ + if (!strName.equalsIgnoreCase("default")) { + strStore = strName.toLowerCase(); + File filOldPet = new File("pets/" + strStore); + File filOldBackup = new File("pets/" + strStore + ".backup"); + if (filOldPet.exists() && !filPlayer.exists()) { + filOldPet.renameTo(filPlayer); + filOldPet.delete(); + } + if (filOldBackup.exists() && !filBackup.exists()) { + filOldBackup.renameTo(filBackup); + filOldBackup.delete(); + } + } + strStore = this.master.name.toLowerCase(); + int i = 0; + if (filBackup.exists()) { + if (filPlayer.length() > filBackup.length()) { + filCheck = new File("backup/" + strStore + ".possiblyDamaged"); + while (filCheck.exists()) { + i++; + filCheck = new File("backup/" + strStore + ".possiblyDamaged." + i); + } + filBackup.renameTo(filCheck); + } else if (filPlayer.length() < filBackup.length()) { + filCheck = new File("backup/" + strStore + ".possiblyDamaged"); + while (filCheck.exists()) { + i++; + filCheck = new File("backup/" + strStore + ".possiblyDamaged." + i); + } + filPlayer.renameTo(filCheck); + filBackup.renameTo(new File("pets/" + strStore)); + } + } + strStore = this.master.name.toLowerCase(); + + file = new File("pets", strStore); + backup = new File("pets", strStore + ".backup"); + loadUserFile(file); + if (race != null) { + loadRaceFile(new File("defPets/" + race.toLowerCase()), true); + } + if (masterName == null) { + masterName = this.master.name.toLowerCase(); + } + //game.vctPets.addElement(this); + isSaveable = true; + } catch (Exception e) { + game.log.printError("LivingThing():Creating pet", e); + close(); + } + } + + public LivingThing getFollowing() { + return following; + } + + public LivingThing getMaster() { + return master; + } + + public LivingThing getCharmer() { + return charmer; + } + + public void setFollowing(LivingThing following) { + this.following = following; + } + + public void setMaster(LivingThing master) { + this.master = master; + } + + public void leaveBattle() { + battle = null; + battleSide = 0; + isMoveable = true; + } + + public void enterBattle(Battle battle, int sideid) { +// thnAdded.changeLocBypass(thnFront1.intLocX,thnFront1.intLocY); + isMoveable = false; + isSleeping = false; + this.battle = battle; + updateActions(); + battleSide = (byte) sideid; + } + + private void setAbility(Map map, String name, int value) { + map.put(name.toLowerCase(), new Ability(value, name)); + } + + private int getAbility(Map map, String name) { + Ability ability = map.get(name.toLowerCase()); + + return ability != null ? ability.getAbility() : 0; + } + + public int getSkill(String name) { + return getAbility(skillMap, name); + } + + public int getSpell(String name) { + return getAbility(spellMap, name); + } + + /** + * Train a skill or spell. + * + * @param name + * @param add increment + * @return true if the skill/spell exists and was created/incremented + */ + public boolean addToSkill(String name, int add) { + System.out.println("adding to skill " + name + " " + add); + String key = name.toLowerCase(); + try { + Ability skill = spellMap.get(key); + if (skill == null) { + File filCheck = new File("defSpellGroups/" + name); + if (filCheck.exists()) { + setAbility(spellMap, name, add); + return true; + } + if ((skill = skillMap.get(name)) == null) { + setAbility(skillMap, name, add); + return true; + } + } + return skill.train(add); + } catch (Exception e) { + game.log.printError("addToSkill()", e); + } + return false; + } + + public boolean setSkill(String name, int value) { + String key = name.toLowerCase(); + if (value <= 0) { + if (skillMap.remove(key) != null) { + return true; + } + if (spellMap.remove(key) != null) { + return true; + } + return false; + } + // TODO: try/catch not needed here + try { + + Ability skill = spellMap.get(key); + if (skill == null) { + File filCheck = new File("defSpellGroups/" + name); + if (filCheck.exists()) { + setAbility(spellMap, name, value); + return true; + } + if ((skill = skillMap.get(name)) == null) { + setAbility(skillMap, name, value); + return true; + } + } + skill.setAbility(value); + return true; + } catch (Exception e) { + game.log.printError("setSkill()", e); + } + return false; + } + + int totalSkillValue() { + int total = 0; + + for (Ability skill : skillMap.values()) + total += skill.getAbility(); + for (Ability spell : spellMap.values()) + total += spell.getAbility(); + return total; + } + + public void loadUserFile(File path) throws FileNotFoundException, IOException { + String line; + try (RandomAccessFile file = new RandomAccessFile(path, "r")) { + while (!((line = file.readLine()) == null || line.equals("."))) { + try { + parseUserFile(file, line); + } catch (NumberFormatException x) { + throw new IOException("Problem parsing user " + path + " on field " + line, x); + } + } + } + } + + protected void parseWear(RandomAccessFile in, int where) throws IOException { + Item item = game.getItem(in.readLine()); + if (item != null) { + item.lngDurability = Long.parseLong(in.readLine()); + item.intUses = Integer.parseInt(in.readLine()); + wornItems.wear(where, item); + onWear(item); + } + } + + protected void parseUserFile(RandomAccessFile in, String type) throws IOException, NumberFormatException { + switch (type.toLowerCase()) { + case "timestamp": + lastSaveStamp = Long.parseLong(in.readLine()); + break; + case "petname": + name = in.readLine(); + break; + case "master": + masterName = in.readLine(); + break; + case "skill": + type = in.readLine(); + addToSkill(type, Byte.parseByte(in.readLine())); + break; + case "condition": { + // FIXME: i/o to Condition + Condition cndStore = game.getCondition(in.readLine()); + cndStore.ticksPast = Integer.parseInt(in.readLine()); + cndStore.duration = Integer.parseInt(in.readLine()); + if (cndStore.duration < -1) { // only necessary to repair + cndStore.duration = -1; // after bug fix, can go away + } + addCondition(cndStore); + break; + } + case "item": { + Item itmStore = game.getItem(in.readLine()); + if (itmStore != null) { + itemList.addElement(itmStore); + } + break; + } + case "item2": { //for compatibility. Will be replaced with item later + Item itmStore = game.getItem(in.readLine()); + if (itmStore != null) { + itmStore.lngDurability = Long.parseLong(in.readLine()); + itmStore.intUses = Integer.parseInt(in.readLine()); + itemList.addElement(itmStore); + } + break; + } + case "privs": + privs = Byte.parseByte(in.readLine()); + break; + case "clan": + clan = in.readLine(); + break; + case "race": + race = in.readLine(); + break; + case "title": + title = in.readLine(); + break; + case "description": + description = in.readLine(); + break; + case "x": + x = Integer.parseInt(in.readLine()); + break; + case "y": + y = Integer.parseInt(in.readLine()); + break; + case "hp": + hp = Integer.parseInt(in.readLine()); + break; + case "maxhp": + maxhp = Integer.parseInt(in.readLine()); + break; + case "mp": + case "sp": + mp = Integer.parseInt(in.readLine()); + break; + case "maxmp": + case "maxsp": + maxmp = Integer.parseInt(in.readLine()); + break; + case "cash": + cash = Integer.parseInt(in.readLine()); + break; + case "exp": + exp = Integer.parseInt(in.readLine()); + break; + case "stre": + stre = Integer.parseInt(in.readLine()); + break; + case "inte": + inte = Integer.parseInt(in.readLine()); + break; + case "dext": + dext = Integer.parseInt(in.readLine()); + break; + case "cons": + cons = Integer.parseInt(in.readLine()); + break; + case "wisd": + wisd = Integer.parseInt(in.readLine()); + break; + case "image": + imageid = Integer.parseInt(in.readLine()); + break; + case "pet": + if (!isPet()) { + following = new LivingThing(in.readLine(), null, this, game); + } + break; + // FIXME: do i need these 'old' versions? + case "wield": + wornItems.wear(Equipment.WIELD, game.getItem(in.readLine())); + break; + case "arms": + wornItems.wear(Equipment.ARMS, game.getItem(in.readLine())); + break; + case "legs": + wornItems.wear(Equipment.LEGS, game.getItem(in.readLine())); + break; + case "torso": + wornItems.wear(Equipment.TORSO, game.getItem(in.readLine())); + break; + case "waist": + wornItems.wear(Equipment.WAIST, game.getItem(in.readLine())); + break; + case "neck": + wornItems.wear(Equipment.NECK, game.getItem(in.readLine())); + break; + case "skull": + wornItems.wear(Equipment.SKULL, game.getItem(in.readLine())); + break; + case "eyes": + wornItems.wear(Equipment.EYES, game.getItem(in.readLine())); + break; + case "hands": + wornItems.wear(Equipment.HANDS, game.getItem(in.readLine())); + break; + case "wield2": + parseWear(in, Equipment.WIELD); + break; + case "arms2": + parseWear(in, Equipment.ARMS); + break; + case "legs2": + parseWear(in, Equipment.LEGS); + break; + case "torso2": + parseWear(in, Equipment.TORSO); + break; + case "waist2": + parseWear(in, Equipment.WAIST); + break; + case "neck2": + parseWear(in, Equipment.NECK); + break; + case "skull2": + parseWear(in, Equipment.SKULL); + break; + case "eyes2": + parseWear(in, Equipment.EYES); + break; + case "hands2": + parseWear(in, Equipment.HANDS); + break; + case "nofollow": + noFollow = true; + break; + case "nopopup": + popup = false; + break; + case "nochannel": + noChannel = Integer.parseInt(in.readLine()); + break; + case "audiooff": + audioon = false; + break; + case "coloroff": + coloron = false; + break; + } + // engGame.log.printError("parseUserFile():Parsing \"" + strStore + "\" from " + name + "'s file", e); + } + + public void loadRaceFile(File path, boolean add) throws FileNotFoundException, IOException { + String line; + try (RandomAccessFile file = new RandomAccessFile(path, "r")) { + while (!((line = file.readLine()) == null || line.equals("."))) { + try { + parseRaceFile(file, line, add); + } catch (NumberFormatException x) { + throw new IOException("Problem parsing race " + path + " on field " + line, x); + } + } + } + } + + protected void parseRaceFile(RandomAccessFile file, String strStore, boolean add) { + int s = add ? 1 : -1; + try { + switch (strStore) { + case "hp": + hpbon += s * Integer.parseInt(file.readLine()); + break; + case "mp": + mpbon += s * Integer.parseInt(file.readLine()); + break; + case "stre": + strebon += s * Integer.parseInt(file.readLine()); + break; + case "inte": + intebon += s * Integer.parseInt(file.readLine()); + break; + case "dext": + dextbon += s * Integer.parseInt(file.readLine()); + break; + case "cons": + consbon += s * Integer.parseInt(file.readLine()); + break; + case "wisd": + wisdbon += s * Integer.parseInt(file.readLine()); + break; + case "range": + rangebon += s * Integer.parseInt(file.readLine()); + break; + case "hp_limit": + hp_limit = add ? Integer.parseInt(file.readLine()) : -1; + break; + case "mp_limit": + mp_limit = add ? Integer.parseInt(file.readLine()) : -1; + break; + case "exp_limit": + exp_limit = add ? Integer.parseInt(file.readLine()) : -1; + break; + case "stre_limit": + stre_limit = add ? Integer.parseInt(file.readLine()) : 100; + break; + case "inte_limit": + inte_limit = add ? Integer.parseInt(file.readLine()) : 100; + + break; + case "dext_limit": + dext_limit = add ? Integer.parseInt(file.readLine()) : 100; + break; + case "cons_limit": + cons_limit = add ? Integer.parseInt(file.readLine()) : 100; + break; + case "wisd_limit": + wisd_limit = add ? Integer.parseInt(file.readLine()) : 100; + break; + case "image": + imageid = Integer.parseInt(file.readLine()); + break; + } + } catch (Exception e) { + game.log.printError("parseRaceFile():Parsing \"" + strStore + "\" from " + name + "'s race file", e); + } + } + + void writePlayerFile(RandomAccessFile rafPlayerFile) throws IOException { + synchronized (rafPlayerFile) { + rafPlayerFile.setLength(0); + if (isPlayer()) { + rafPlayerFile.writeBytes(password + "\n"); + rafPlayerFile.writeBytes("privs\n" + String.valueOf(privs) + "\n"); + if (title != null) { + rafPlayerFile.writeBytes("title\n" + title + "\n"); + } + if (clan != null) { + rafPlayerFile.writeBytes("clan\n" + clan + "\n"); + } else { + rafPlayerFile.writeBytes("clan\nnone\n"); + } + rafPlayerFile.writeBytes("x\n" + String.valueOf(x) + "\n"); + rafPlayerFile.writeBytes("y\n" + String.valueOf(y) + "\n"); + if (popup == false) { + rafPlayerFile.writeBytes("nopopup\n"); + } + if (audioon == false) { + rafPlayerFile.writeBytes("audiooff\n"); + } + if (coloron == false) { + rafPlayerFile.writeBytes("coloroff\n"); + } + if (noFollow == true) { + rafPlayerFile.writeBytes("nofollow\n"); + } + if (noChannel != 0) { + rafPlayerFile.writeBytes("nochannel\n" + noChannel + "\n"); + } + } + rafPlayerFile.writeBytes("race\n" + race + "\n"); + if (isPet()) { + rafPlayerFile.writeBytes("petname\n" + name + "\n"); + rafPlayerFile.writeBytes("master\n" + masterName + "\n"); + } + if (description != null) { + rafPlayerFile.writeBytes("description\n" + description + "\n"); + } + rafPlayerFile.writeBytes("exp\n" + String.valueOf(exp) + "\n"); + rafPlayerFile.writeBytes("hp\n" + String.valueOf(hp) + "\n"); + rafPlayerFile.writeBytes("maxhp\n" + String.valueOf(maxhp) + "\n"); + rafPlayerFile.writeBytes("mp\n" + String.valueOf(mp) + "\n"); + rafPlayerFile.writeBytes("maxmp\n" + String.valueOf(maxmp) + "\n"); + rafPlayerFile.writeBytes("cash\n" + String.valueOf(cash) + "\n"); + rafPlayerFile.writeBytes("stre\n" + String.valueOf(stre) + "\n"); + rafPlayerFile.writeBytes("inte\n" + String.valueOf(inte) + "\n"); + rafPlayerFile.writeBytes("dext\n" + String.valueOf(dext) + "\n"); + rafPlayerFile.writeBytes("cons\n" + String.valueOf(cons) + "\n"); + rafPlayerFile.writeBytes("wisd\n" + String.valueOf(wisd) + "\n"); + + for (Ability skill : skillMap.values()) + rafPlayerFile.writeBytes("skill\n" + skill.name + "\n" + skill.getAbility() + "\n"); + for (Ability spell : spellMap.values()) + rafPlayerFile.writeBytes("skill\n" + spell.name + "\n" + spell.getAbility() + "\n"); + for (Condition cond : conditions) { + rafPlayerFile.writeBytes("condition\n" + cond.name + "\n" + cond.ticksPast + "\n" + cond.duration + "\n"); + } + for (int i = 0; i < Equipment.WEARING_COUNT; i++) { + Item item = wornItems.getWorn(i); + if (item != null) { + rafPlayerFile.writeBytes(Equipment.USER_NAMES[i] + "\n" + item.name + "\n" + item.lngDurability + "\n" + item.intUses + "\n"); + } + } + for (LinkedList list : itemList.values()) { + for (Item item : list) { + rafPlayerFile.writeBytes("item2\n" + item.name + "\n" + item.lngDurability + "\n" + item.intUses + "\n"); + } + } + rafPlayerFile.writeBytes(".\n");//end of file + } + } + + public void savePlayer() { + if (!isSaveNeeded) { + /* + ** User info has not changed significantly + ** No need to save at this time. + */ + return; + } + if (!isSaveable) { + /* + ** File is currently loading or saving and is not safe to + ** save at this time. + */ + return; + } + isSaveable = false; + + String strStore = name.toLowerCase(); + String strDirName = "users"; + if (isPet()) { + strStore = masterName; + strDirName = "pets"; + } + + // This should work if it's going to, write out a whole file, + // and if that succeeds, copy it over + + try (RandomAccessFile rafFile = new RandomAccessFile(backup, "rw")) { + writePlayerFile(rafFile); + } catch (IOException x) { + game.log.printMessage(Log.ERROR, "savePlayer():Saving primary file for " + backup + ", failed, aborting savePlayer"); + isSaveable = true; + return; + } + + try { + Files.move(backup.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException ex) { + game.log.printMessage(Log.ERROR, "savePlayer():Renaming primary file for " + file + ", failed, aborting savePlayer"); + } + + /* + + boolean blnSaveSuccessful; + long lngFileLength = 0; + blnSaveSuccessful = writePlayerFile(rafFile); + try { + lngFileLength = rafFile.length(); + } catch (Exception e) { + } + + if (!blnSaveSuccessful || (lngFileLength < 100)) { + // Error saving file. Close it, open it back up, and try again. + try { + rafFile.close(); + } catch (Exception e) { + } + try { + rafFile = new RandomAccessFile(strDirName + "/" + strStore, "rw"); + } catch (Exception e) { + } + + blnSaveSuccessful = writePlayerFile(rafFile); + + try { + lngFileLength = rafFile.length(); + } catch (Exception e) { + } + + if (!blnSaveSuccessful || (lngFileLength < 100)) { + engGame.log.printMessage(Log.ERROR, "savePlayer():Saving primary file for " + name + ", second try failed, aborting savePlayer"); + blnCanSave = true; + return; + } + } + + blnShouldSave = false; + + blnSaveSuccessful = writePlayerFile(rafBackup); + + try { + lngFileLength = rafBackup.length(); + } catch (Exception e) { + } + + if (!blnSaveSuccessful || (lngFileLength < 100)) { + // Error saving file. Close it, open it back up, and try again. + try { + rafBackup.close(); + } catch (Exception e) { + } + try { + rafBackup = new RandomAccessFile(strDirName + "/" + strStore + ".backup", "rw"); + } catch (Exception e) { + } + + blnSaveSuccessful = writePlayerFile(rafBackup); + + try { + lngFileLength = rafBackup.length(); + } catch (Exception e) { + } + + if (!blnSaveSuccessful || (lngFileLength < 100)) { + engGame.log.printMessage(Log.ERROR, "savePlayer():Saving backup file for " + name + ", second try failed, aborting savePlayer"); + blnCanSave = true; + return; + } + } + */ + isSaveable = true; + } + + public void close() { + if (name == null || name.equalsIgnoreCase("default")) { + return; + } + if (isClosing) { + /* + ** Already closing elsewhere + */ + return; + } + isClosing = true; + isWorking = false; + isSaveNeeded = true; + if (!conditions.isEmpty() && game.checkConditionList.contains(this)) { + game.checkConditionList.remove(this); + } + removeFromGroup(); + try { + game.removeDuskObject(this); + if (isPlayer()) { + try { + synchronized (game.scrOnLogOut) { + if (isSaveable) { + game.scrOnLogOut.varVariables.clearVariables(); + game.scrOnLogOut.varVariables.addVariable("trigger", this); + game.scrOnLogOut.runScript(); + } + } + } catch (Exception e) { + } + try { + outstream.writeBytes((char) 3 + "Goodbye.\n" + (char) 0); + } catch (Exception e) { + } + synchronized (game.playersByName) { + game.playersByName.remove(name.toLowerCase()); + } + try { + savePlayer(); + } catch (Exception e) { + } + isSaveable = false; + //try { + // rafFile.close(); + // rafBackup.close(); + //} catch (Exception e) { + //} + try { + if (following != null && following.isPet()) { + following.close(); + } + for (LivingThing pet : game.petList) { + if (pet.master == this) { + pet.close(); + break; + } + } + } catch (Exception e) { + } + try { + instream.close(); + } catch (Exception e) { + } + try { + outstream.close(); + } catch (Exception e) { + } + if (!(hasCondition("invis") && (privs > 2))) { + game.log.printMessage(Log.INFO, socket.getInetAddress().toString() + ":" + name + " has logged out"); + game.chatMessage(name + " has logged out.", name); + } else { + game.log.printMessage(Log.INFO, socket.getInetAddress().toString() + ":" + name + " has logged out hidden from players"); + } + try { + socket.close(); + } catch (Exception e) { + } + isStopped = true; + } else if (isMob()) { + //try { + // rafFile.close(); + // rafBackup.close(); + //} catch (Exception e) { + //} + //game.vctMobs.remove(this); + } else if (isPet()) { + //game.vctPets.removeElement(this); + savePlayer(); + //try { + // rafFile.close(); + // rafBackup.close(); + //} catch (Exception e) { + //} + } + } catch (Exception e) { + game.log.printError("close():While closing " + name, e); + } + } + + public void closeNoMsgPlayer() { + // Disabled this check... if this is called, the player needs to be closed. + // if (blnIsClosing) + // { + // /* + // ** Already closing elsewhere + // */ + // return; + // } + isClosing = true; + isWorking = false; + removeFromGroup(); + if (conditions.size() != 0) { + game.checkConditionList.remove(this); + } + game.removeDuskObject(this); + game.playersByName.remove(name.toLowerCase()); + try { + socket.shutdownInput(); + } catch (Exception e) { + } + try { + socket.shutdownOutput(); + } catch (Exception e) { + } + try { + instream.close(); + } catch (Exception e) { + } + savePlayer(); + //try { + // rafFile.close(); + // rafBackup.close(); + //} catch (Exception e) { + //} + if (following != null && following.isPet()) { + following.close(); + } + try { + outstream.close(); + } catch (Exception e) { + } + isStopped = true; + game.log.printMessage(Log.INFO, socket.getInetAddress().toString() + ":" + name + " has logged out"); + game.chatMessage(name + " has logged out.", name); + try { + socket.close(); + } catch (Exception e) { + } + name = null; + } + + void closeNosavePlayer() { + if (name == null || name.equalsIgnoreCase("default")) { + isStopped = true; + try { + instream.close(); + } catch (Exception e) { + } + try { + socket.close(); + } catch (Exception e) { + } + try { + outstream.close(); + } catch (Exception e) { + } + return; + } + if (isClosing) { + /* + ** Already closing elsewhere + */ + return; + } + isClosing = true; + isWorking = false; + isSaveable = false; + isSaveNeeded = false; + if (!conditions.isEmpty() && game.checkConditionList.contains(this)) { + game.checkConditionList.remove(this); + } + removeFromGroup(); + try { + game.removeDuskObject(this); + if (isPlayer()) { + try { + synchronized (game.scrOnLogOut) { + //engGame.scrOnLogOut.varVariables.clearVariables(); + //engGame.scrOnLogOut.varVariables.addVariable("trigger",this); + //engGame.scrOnLogOut.runScript(); + } + } catch (Exception e) { + } + try { + outstream.writeBytes((char) 3 + "Goodbye.\n" + (char) 0); + } catch (Exception e) { + } + //try { + // rafFile.close(); + // rafBackup.close(); + //} catch (Exception e) { + //} + game.playersByName.remove(name.toLowerCase()); + try { + instream.close(); + } catch (Exception e) { + } + try { + outstream.close(); + } catch (Exception e) { + } + try { + socket.close(); + } catch (Exception e) { + } + isStopped = true; + } else if (isMob()) { + //game.vctMobs.remove(this); + } else if (isPet()) { + //game.vctPets.removeElement(this); + //try { + // rafFile.close(); + // rafBackup.close(); + //} catch (Exception e) { + //} + } + } catch (Exception e) { + game.log.printError("closeNosavePlayer():While closing " + name, e); + } + } + + public void updateFlag(long ID, int Value) { + send((char) 29 + "" + ID + "\n" + Value + "\n"); + } + + public void clearFlags() { + send((char) 30 + ""); + } + + public boolean hasPendingMoves() { + synchronized (moveQueue) { + return !moveQueue.isEmpty(); + } + } + + /** + * Clear any pending moves and queue a new move direction + * + * @param dir n/s/e/w + * @return true if move is possible + */ + public boolean moveAfterClear(String dir) { + if (getMaster() != null) { + if (!isPet() || getMaster().getFollowing() == this) { + return false; + } + } + synchronized (moveQueue) { + moveQueue.clear(); + moveQueue.add(dir); + } + return true; + } + + public void clearMoveQueue() { + synchronized (moveQueue) { + moveQueue.clear(); + } + } + + /** + * Process one queued move + */ + public void moveTick() { + synchronized (moveQueue) { + if (moveQueue.isEmpty()) + return; + + String dir = moveQueue.removeFirst(); + + if (dir != null) { + switch (dir.charAt(0)) { + case 'n': + moveN(); + break; + case 's': + moveS(); + break; + case 'w': + moveW(); + break; + case 'e': + moveE(); + break; + } + } + } + } + + /** + * Send this to a specific destination. + * + * This is a completely new implementation using the map + * + * @param destX + * @param destY + * @param goon If true, go to exact location, otherwise only next to it + * @return + */ + public String goTo(int destX, int destY, boolean goon) { + if (master != null) { + if (!isPet() || master.following == this) { + return "You can't move while you're following someone."; + } + } + if (goon && !game.canMoveTo(destX, destY, this)) { + return "You can't move onto that location."; + } + if (Math.abs(destX - x) > game.viewrange || Math.abs(destY - y) > game.viewrange) { + return null; + } + synchronized (moveQueue) { + moveQueue.clear(); + + TileMap.MoveListener ml = new TileMap.MoveListener() { + @Override + public boolean canMoveto(MapData md) { + return game.canMoveTo(md.x, md.y, LivingThing.this); + } + }; + int mflags = goon ? 0 : TileMap.SKIP_END; + + for (MoveData md : game.map.move(x, y, destX, destY, mflags, ml)) { + moveQueue.add(md.direction); + } + } + return null; + } + + // FIXME: should probably be in DuskEngine + protected synchronized void moveTo(int newLocX, int newLocY, MessageType dir, int intNewStep) { + if (privs < 5 && (newLocX >= (game.map.getCols() - 1) + || newLocY >= (game.map.getRows() - 1) + || newLocX < 0 + || newLocY < 0)) { + return; + } + // Movement during combat is disallowed here + // need to check min range in the battle and see if anything is at 1 with this livingthing +// if (privs>1 || (batBattle == null && !blnSleep && engGame.canMoveTo(newLocX,newLocY,this))) + if (privs > 1 || (isMoveable && !isSleeping && game.canMoveTo(newLocX, newLocY, this))) { + if (isMob()) { + int maxY = originalY + game.viewrange; + int minY = originalY - game.viewrange; + int maxX = originalX + game.viewrange; + int minX = originalX - game.viewrange; + if ((newLocY > maxY) || (newLocY < minY) || (newLocX > maxX) || (newLocX < minX)) { + return; + } + } + } else { + return; + } + int oldLocX = x; + int oldLocY = y; + // Move it from cell to cell + game.moveDuskObject(this, newLocX, newLocY, dir); + try { + imagestep = intNewStep; + if (isPlayer()) { + if (game.overMerchant(oldLocX, oldLocY) != null) { + offMerchant(); + } + if (game.overPlayerMerchant(oldLocX, oldLocY) != null) { + offMerchant(); + } + updateMap(); + } + Script scrStore; + try { + scrStore = (Script) game.tileAction.get((int) game.map.getTile(x, y)); + synchronized (scrStore) { + scrStore.varVariables.clearVariables(); + scrStore.varVariables.addVariable("trigger", this); + scrStore.runScript(); + } + } catch (Exception e) { + } + Script.exec("defMoveActions/" + x + "_" + y, game, this); + + //move follower + if (following != null) { + //don't move follower if leader has moved onto follower's tile + if ((following.x == newLocX) && (following.y == newLocY)) { + return; + } + if (Math.abs(following.x - oldLocX) + Math.abs(following.y - oldLocY) > 1) { + if (following.isPet()) { + if (following.following != null) { + chatMessage(following.following.name + " is no longer following you."); + following.following.chatMessage("You are no longer following " + name + "."); + following.following.master = null; + following.following = null; + } + following.changeLocBypass(oldLocX, oldLocY); + } else { + chatMessage(following.name + " is no longer following you."); + following.chatMessage("You are no longer following " + name + "."); + following.master = null; + following = null; + } + } else if (following.y > oldLocY) { + following.moveN(); + } else if (following.y < oldLocY) { + following.moveS(); + } else if (following.x > oldLocX) { + following.moveW(); + } else if (following.x < oldLocX) { + following.moveE(); + } + } + if (isPlayer()) { + game.refreshEntities(this); + } + //game.removeIfCanNoLongerSee(this); + } catch (Exception e) { + game.log.printError("moveTo()", e); + } + } + + public void moveN() { + moveTo(x, y - 1, MessageType.MoveNorth, 0); + } + + public void moveS() { + moveTo(x, y + 1, MessageType.MoveSouth, 2); + } + + public void moveW() { + moveTo(x - 1, y, MessageType.MoveWest, 4); + } + + public void moveE() { + moveTo(x + 1, y, MessageType.MoveEast, 6); + } + + public DuskObject removeEntity(long id) { + synchronized (nearEntities) { + DuskObject o = nearEntities.remove(id); + + if (o != null) { + System.out.println("Removing client entity " + id + " from " + name); + send(MessageType.RemoveEntity, id + "\n"); + } + return o; + } + } + + public boolean nearEntity(long id) { + synchronized (nearEntities) { + return nearEntities.containsKey(id); + } + } + + public void addEntity(DuskObject ent) { + synchronized (nearEntities) { + if (nearEntities.containsKey(ent.ID)) + return; + + System.out.println("Adding client entity " + ent.ID + " " + ent.name + " to " + name); + + nearEntities.put(ent.ID, ent); + } + + // Only sent to player if they can see it? + if (!ent.isLivingThing() + || game.canSeeLivingThing(this, (LivingThing) ent)) { + System.out.println("Adding entity " + ent.name + " to " + name); + //send(MessageType.AddEntity, ent.toEntity()); + send(ent.toMessage(MessageType.AddEntity)); + } else { + System.out.println("Not adding entity " + ent.name + " to " + name); + } + } + + /** + * Set a new set of near-entities + * + * @param ent + */ + public void setEntities(List newEntities) { + HashMap left = (HashMap) nearEntities.clone(); + + if (false) { + System.out.println("setting entities on " + name); + for (DuskObject d : left.values()) { + System.out.println(" - " + d.ID + " " + d.name); + } + for (DuskObject d : newEntities) { + System.out.println(" + " + d.ID + " " + d.name); + } + } + + for (DuskObject o : newEntities) { + if (left.containsKey(o.ID)) { + // Have this already, do nothing + left.remove(o.ID); + } else { + // This is a new one + //nearEntities.put(o.ID, o); + game.addEntity(o); + } + } + // anything left over must be removed + for (long id : left.keySet()) { + removeEntity(id); + } + } + + public void changeLoc(int newLocX, int newLocY) { + if (privs > 1 || (battle == null && !isSleeping && game.canMoveTo(newLocX, newLocY, this))) { + if (isMob()) { + if (!(Math.abs(originalX - newLocX) < game.viewrange && Math.abs(originalY - newLocY) < game.viewrange)) { + return; + } + } + changeLocBypass(newLocX, newLocY); + } + } + + synchronized public void changeLocBypass(int newLocX, int newLocY) { + int oldLocX = x; + int oldLocY = y; + game.removeDuskObject(this); + x = newLocX; + y = newLocY; + try { + if (isPlayer()) { + updateMap(); + if (game.overMerchant(oldLocX, oldLocY) != null) { + offMerchant(); + } + if (game.overPlayerMerchant(oldLocX, oldLocY) != null) { + offMerchant(); + } + } + //update entity: + if (isWorking) { + game.addDuskObject(this); + } + Script scrStore; + try { + scrStore = (Script) game.tileAction.get((int) game.map.getTile(newLocX, newLocY)); + synchronized (scrStore) { + scrStore.varVariables.clearVariables(); + scrStore.varVariables.addVariable("trigger", this); + scrStore.runScript(); + } + } catch (Exception e) { + } + Script.exec("defMoveActions/" + newLocX + "_" + newLocY, game, this); + if (following != null) { + following.changeLocBypass(oldLocX, oldLocY); + } + if (isPlayer()) { + game.refreshEntities(this); + } + } catch (Exception e) { + game.log.printError("changeLocBypass():" + name + " disconnected", e); + isStopped = true; + } + } + + public void updateMap() { + /* + StringBuilder sb = new StringBuilder(); + //update map: + + sb.append(x).append("\n"); + sb.append(y).append("\n"); + + int r = game.viewrange; + for (int mx = x - r; mx <= x + r; mx++) { + for (int my = y - r; my <= y + r; my++) { + if (game.map.inside(mx, my)) + sb.append(game.map.getTile(mx, my)).append("\n"); + else + sb.append("0\n"); + } + } + + send(MessageType.UpdateLocMap, sb.toString());*/ + + int r = game.viewrange; + + short[] tiles = new short[(r * 2 + 1) * (r * 2 + 1)]; + int i = 0; + //System.out.printf("map at %d,%d\n", x, y); + for (int my = y - r; my <= y + r; my++) { + for (int mx = x - r; mx <= x + r; mx++) { + tiles[i++] = game.map.inside(mx, my) ? game.map.getTile(mx, my) : 0; + //System.out.printf("%s%3d", game.map.inside(mx, my) ? " " : "*", tiles[i-1]); + } + //System.out.println("\n"); + } + + send(ServerMessage.mapMessage(x, y, tiles)); + } + + public void chatMessage(String inMessage) { + if (inMessage == null) { + return; + } + if (isPlayer()) { + send(MessageType.Chat, inMessage + "\n"); + } + if (charmer != null) { + charmer.chatMessage("From " + name + ": " + inMessage); + return; + } + } + + public void chatMessage(int red, int green, int blue, String inMessage) { + if (inMessage == null) { + return; + } + if (isPlayer()) { + if (!coloron) { + chatMessage(inMessage); + return; + } + String strResult = "" + (char) 23; + strResult += red + "\n" + green + "\n" + blue + "\n" + inMessage + "\n"; + send(strResult); + } + if (charmer != null) { + charmer.chatMessage(red, green, blue, "From " + name + ": " + inMessage); + return; + } + } + + public int getCharacterPoints() { + int result = wisd + + inte + + stre + + dext + + cons + + ((maxhp) / 10) + + ((maxmp) / 10) + + totalSkillValue(); + return result; + } + + public int getTotalPoints() { + int result = getCharacterPoints() + + getArmorMod() + + (getDamMod() - 100); + return result; + } + + public Item getItem(String strStore) { + // Check worn items by location, TBH not really sure of the point of this + int where = Equipment.toIndex(strStore); + if (where != -1) { + Item item = wornItems.getWorn(where); + if (item != null) + return item; + } + + int i, + intNumber = 1; + i = strStore.indexOf('.'); + if (i != -1) { + try { + intNumber = Integer.parseInt(strStore.substring(0, i)); + strStore = strStore.substring(i + 1, strStore.length()); + } catch (NumberFormatException e) { + intNumber = 1; + } + } + Item itmStore; + LinkedList qStore; + qStore = itemList.get(strStore); + if (qStore != null) { + if (qStore.size() >= intNumber) { + itmStore = (Item) qStore.element(); + return itmStore; + } + } + return null; + } + + public Item getItemAndRemove(String strStore) { + int i, + intNumber = 1; + i = strStore.indexOf("."); + if (i != -1) { + try { + intNumber = Integer.parseInt(strStore.substring(0, i)); + strStore = strStore.substring(i + 1, strStore.length()); + } catch (NumberFormatException e) { + intNumber = 1; + } + } + if (intNumber == 0) { + return null; + } + Item itmStore; + Item itmFound = null; + LinkedList qStore; + qStore = itemList.get(strStore); + if (qStore != null) { + if (!qStore.isEmpty()) { + itmStore = (Item) qStore.element(); + itmFound = itmStore; + while (intNumber != 0 && itmStore != null) { + qStore.pop(); + intNumber--; + } + if (qStore.size() < 1) { + itemList.removeElement(strStore); + } + } else { + itemList.removeElement(strStore); + } + } + return itmFound; + } + + public int getArmorMod() { + return wornItems.armourMod(); + } + + public int getArmorModWithBonus() { + return getArmorMod() + ((dext + dextbon) / 10) + acbon; + } + + public int getDamMod() { + Item item = wornItems.getWorn(Equipment.WIELD); + if (item != null) + return item.intMod; + return 100; + } + + public int getDamModWithBonus() { + return getDamMod() + dammodbon; + } + + public int getRange() { + Item item = wornItems.getWorn(Equipment.WIELD); + + if (item == null) { + return 1; + } + return item.range(); + } + + public int getRangeWithBonus() { + return getRange() + rangebon; + } + + public void weaponDam(int damage) { + if (isMob()) //Mobs weapons are invincible + { + return; + } + + Item item = wornItems.damageItem(Equipment.WIELD, damage); + if (item != null) { + chatMessage("Your " + item.name + " breaks."); + onUnwear(item); + if (isPlayer()) { + updateEquipment(); + updateStats(); + } + } + } + + public void armorDam(int damage) { + if (isMob()) //Mobs armor is invincible + { + return; + } + int armour = wornItems.armourCount(); + if (armour == 0) { + return; + } + + for (int i = Equipment.ARMS; i < Equipment.WEARING_COUNT; i++) { + Item item = wornItems.damageItem(Equipment.WIELD, damage); + if (item != null) { + chatMessage("Your " + item.name + " breaks."); + onUnwear(item); + updateStats(); + updateEquipment(); + } + } + } + + public void useItem(String strStore, int mustBeType) { + String strStore2 = null; + int i2 = 0; + if (strStore.startsWith("\"")) { + char c, c2; + + strStore2 = ""; + i2 = 1; + try { + while (true) { + c = strStore.charAt(i2); + if (c == '\\') { + c2 = strStore.charAt(i2 + 1); + strStore2 += c2; + } else if (c == '\"') { + i2++; + break; + } else { + strStore2 += c; + } + i2++; + } + } catch (Exception e) { + chatMessage("You don't have that."); + return; + } + } else { + i2 = strStore.indexOf(" "); + if (i2 == -1) { + strStore2 = strStore; + i2 = strStore.length(); + } else { + strStore2 = strStore.substring(0, i2); + } + } + if (strStore.length() > i2 + 1) { + strStore = strStore.substring(i2 + 1); + } + Item itmStore = getItem(strStore2); + if (itmStore == null) { + chatMessage("You don't have that."); + return; + } + if (itmStore.strOnUseScript == null) { + chatMessage("That has no use."); + return; + } + if (mustBeType != -1 && itmStore.intType != mustBeType) { + chatMessage("You can't use that like that."); + return; + } + if (itmStore.intUses == 0) { + chatMessage("That item is used up."); + return; + } + try { + Script scrStore = new Script("scripts/" + itmStore.strOnUseScript, game, false); +// synchronized(scrStore) +// { + scrStore.varVariables.addVariable("trigger", this); + scrStore.varVariables.addVariable("itemname", itmStore.name); + scrStore.varVariables.addVariable("mod", itmStore.intMod); + scrStore.runScript(); + scrStore.close(); +// } + } catch (Exception e) { + chatMessage("That has no use."); + return; + } + itmStore.intUses--; + } + + public void castSpell(String spell) { + String strStore = "", + strStore2 = ""; + int intStore, + intStore2; + LivingThing target = null; + // FIXME: de-quote and/or command parser + int i2 = 0; + if (spell.startsWith("\"")) { + char c, c2; + i2 = 1; + try { + while (true) { + c = spell.charAt(i2); + i2++; + if (c == '\"') { + break; + } + if (c == '\\') { + c2 = spell.charAt(i2); + strStore2 += c2; + } else { + strStore2 += c; + } + } + } catch (Exception e) { + chatMessage("You don't know that spell."); + } + } else { + i2 = spell.indexOf(" "); + if (i2 == -1) { + strStore2 = spell; + i2 = spell.length(); + } else { + strStore2 = spell.substring(0, i2); + } + } + if (spell.length() > i2 + 1) { + spell = spell.substring(i2 + 1); + } else { + spell = null; + } + try { + try (RandomAccessFile rafSpell = new RandomAccessFile("defSpells/" + strStore2.toLowerCase(), "r")) { + strStore = rafSpell.readLine(); + } catch (FileNotFoundException x) { + chatMessage("You don't know that spell."); + return; + } + SpellGroup grpStore = game.getSpellGroup(strStore); + intStore = getSpell(strStore); + intStore2 = grpStore.getSpellNumber(strStore2); + if (grpStore == null) { + chatMessage("You don't know that spell."); + return; + } + // FIXME: reorder this so the script is only created after the checks + Script scrSpell; + try { + scrSpell = new Script("defSpellGroups/" + intStore2 + " " + strStore.toLowerCase(), game, false); + } catch (Exception e) { + chatMessage("You don't know that spell."); + return; + } + if (intStore2 != 0) //change number to percent + { + intStore2 = (100 * intStore2) / (grpStore.vctSpells.size() - 1); + } + if (intStore == 0 || intStore < intStore2) { + chatMessage("You don't know that spell."); + scrSpell.close(); + return; + } + intStore2 = (110 - (intStore - intStore2)) / 2; //change percent to mp cost + if (intStore2 > mp) { + chatMessage("You don't have enough mp to cast that spell."); + scrSpell.close(); + return; + } + mp -= intStore2; + scrSpell.varVariables.addVariable("caster", this); + if (spell == null) { + scrSpell.runScript(); + } else { + scrSpell.runScript(spell); + } + scrSpell.close(); + if (isPlayer()) { + updateInfo(); + } + } catch (Exception e) { + game.log.printError("castSpell()", e); + } + } + + public boolean isWearing(String name) { + return wornItems.isWearing(name); + } + + private void unwear(Item old) { + if (old != null) { + itemList.addElement(old); + onUnwear(old); + } + } + + private void unwear(int index) { + unwear(wornItems.unwear(index)); + } + + public void unWear(String strStore) { + int index = Equipment.toIndex(strStore); + + if (index != -1) { + unwear(index); + } else if (strStore.equalsIgnoreCase("all")) { + for (int i = 0; i < Equipment.WEARING_COUNT; i++) { + unwear(i); + } + } else { + unwear(wornItems.unwearByName(strStore)); + } + if (isPlayer()) { + updateStats(); + updateEquipment(); + updateItems(); + } + } + + public void onWear(Item trigger) { + if (trigger.strOnWearScript != null) { + try { + Script scrStore = new Script("scripts/" + trigger.strOnWearScript, game, false); + scrStore.varVariables.addVariable("trigger", this); + scrStore.varVariables.addVariable("itemname", trigger.name); + scrStore.varVariables.addVariable("mod", trigger.intMod); + scrStore.runScript(); + scrStore.close(); + } catch (Exception e) { + } + } + } + + public void onUnwear(Item trigger) { + if (trigger.strOnUnWearScript != null) { + try { + Script scrStore = new Script("scripts/" + trigger.strOnUnWearScript, game, false); + scrStore.varVariables.addVariable("trigger", this); + scrStore.varVariables.addVariable("itemname", trigger.name); + scrStore.varVariables.addVariable("mod", trigger.intMod); + scrStore.runScript(); + scrStore.close(); + } catch (Exception e) { + } + } + } + + public boolean hasCondition(String strStore) { + for (Condition cndStore : conditions) { + if (cndStore.name.equalsIgnoreCase(strStore)) { + return true; + } + } + return false; + } + + public void addCondition(Condition cndStore) { + if (conditions.isEmpty()) { + game.checkConditionList.add(this); + } else { + for (int i = 0; i < conditions.size(); i++) { + Condition cndStore2 = conditions.get(i); + if (cndStore2.name.equalsIgnoreCase(cndStore.name)) { + conditions.remove(i); + i--; + cndStore2.onEnd(game, this); + } + } + } + conditions.add(cndStore); + cndStore.onStart(game, this); + updateStats(); + } + + public void removeCondition(String strName) { + Condition cndStore; + for (int i = 0; i < conditions.size(); i++) { + cndStore = (Condition) conditions.get(i); + if (strName.equalsIgnoreCase(cndStore.name)) { + conditions.remove(i); + if (conditions.isEmpty()) { + game.checkConditionList.remove(this); + } + cndStore.onEnd(game, this); + updateStats(); + return; + } + } + updateStats(); + } + + public DuskObject getLocalObject(String name) { + return game.findVisibleObject(this, name); + } + + public void removeFromGroup() { + if (following != null && following.isPet()) { + if (master != null) { + try { + chatMessage("You are no longer following " + master.name + "."); + } catch (Exception e) { + } + try { + master.chatMessage(name + " is no longer following you."); + } catch (Exception e) { + } + master.following = following.following; + } + if (following.following != null) { + following.following.master = master; + } + if (!isPet()) { + master = null; + } + following.following = null; + } else { + if (master != null) { + try { + chatMessage("You are no longer following " + master.name + "."); + } catch (Exception e) { + } + try { + master.chatMessage(name + " is no longer following you."); + } catch (Exception e) { + } + master.following = following; + } + if (following != null) { + following.master = master; + } + if (!isPet()) { + master = null; + } + following = null; + } + } + + public void run() { + connectionThread = Thread.currentThread(); + try { + do { + name = instream.readLine(); + if (name != null) { + name = name.trim(); + } + } while (!getPlayer()); + if (!isWorking) { + return; + } + } catch (Exception e) { + game.log.printError("LivingThing.run():start", e); + closeNosavePlayer(); + return; + } + connectionThread.setName("LivingThing(" + name + ")"); + sendThread.setName("LivingThing(" + name + ").send"); + resizeMap(); + changeLocBypass(x, y); + updateInfo(); + updateStats(); + updateItems(); + updateEquipment(); + updateActions(); + if (game.blnMusic) { + updateMusic(); + playMusic(0); + } + if (!(hasCondition("invis") && (privs > 2))) { + game.log.printMessage(Log.INFO, socket.getInetAddress().toString() + ":" + name + " has entered the world"); + game.chatMessage(name + " has entered the world.", name); + } else { + chatMessage("You have entered the world hidden from players."); + game.log.printMessage(Log.INFO, socket.getInetAddress().toString() + ":" + name + " has entered the world hidden from players"); + } + String strInput, + strStore; + isSaveable = true; + isReady = true; + while (true) { + if (isStopped) { + return; + } + try { + if (!isHalted) { + strInput = instream.readLine(); + if (strInput != null) { + strInput = strInput.trim(); + } + strStore = Commands.parseCommand(this, game, strInput); + if (strStore != null) { + chatMessage(strStore); + } + } + } catch (Exception e) { + game.log.printError("LivingThing.run():" + name + " disconnected", e); + close(); + return; + } + } + } + + boolean getPlayer() { + if (!game.isGoodName(name)) { + chatMessage("Not a valid name. This may because you left it blank, used invalid symbols, or made it too long. Please try again."); + return false; + } + String strStore; + int i; + LivingThing thnStore; + try { + File filStore = new File("users/" + name.toLowerCase()); + if (!filStore.exists() || filStore.length() < 100) { + File filBackup = new File("users/" + name.toLowerCase() + ".backup"); + if (filBackup.exists()) { + filBackup.renameTo(filStore); + } + } + + String strIP = socket.getInetAddress().toString(); + int ip = strIP.indexOf("/"); + strIP = strIP.substring(ip + 1, strIP.length()); + + if (filStore.exists()) { + try (RandomAccessFile rafFile = new RandomAccessFile("users/" + name.toLowerCase(), "r")) { + chatMessage("enter your password:"); + password = instream.readLine(); + if (!password.equals(rafFile.readLine())) { + rafFile.close(); + game.log.printMessage(Log.INFO, socket.getInetAddress().toString() + ":" + name + " entered the wrong password"); + Variable failCount = game.varIP.getVariable(strIP); + if (failCount != null) { + int fc = failCount.intValue(); + if (fc >= 4) { + game.banAddress(strIP); + chatMessage("Too many login failures, try again in an hour."); + send("Goodbye.\n" + (char) 0); + Thread.sleep(1000); + closeNosavePlayer(); + rafFile.close(); + return true; + } else { + game.varIP.addVariable(strIP, fc + 1); + } + } else { + game.varIP.addVariable(strIP, (double) 1); + } + Thread.sleep(3000); + chatMessage("Incorrect Password."); + chatMessage("Enter your login name: "); + return false; + } + } catch (Exception e) { + game.log.printError("getPlayer():" + socket.getInetAddress().toString() + ":" + name, e); + closeNosavePlayer(); + return true; + } + } else { + File fileStore = new File("pets/" + name.toLowerCase()); + if (fileStore.exists()) { + chatMessage(name + " is already in use. Please choose another."); + return false; + } + chatMessage(name + ", Is that correct? (yes/no)"); + if (!instream.readLine().equalsIgnoreCase("yes")) { + chatMessage("Then what IS your name?"); + return false; + } + chatMessage("Enter a new password:"); + password = instream.readLine(); + chatMessage("Confirm that password:"); + while (!password.equals(instream.readLine())) { + chatMessage("Passwords did not match, enter a new password:"); + password = instream.readLine(); + chatMessage("Confirm that password:"); + } + } + game.varIP.removeVariable(strIP); + try { + wornItems = new Equipment(); + itemList = new ItemList(); + conditions.clear(); + nearEntities.clear(); + flags.clear(); + ignoreList.clear(); + chatMessage("Login Accepted."); + proceed(); + chatMessage("This game is running DuskServer v" + game.version + ". http://dusk.wesowin.org/"); + chatMessage("Started at " + game.datStart.toString() + "."); + for (LivingThing lt : game.playersByName.values()) { + if (name.equalsIgnoreCase(lt.name)) { + game.log.printMessage(Log.INFO, socket.getInetAddress().toString() + ":" + name + " tried to log in twice"); + chatMessage("That user is already logged in. They are being logged out."); + lt.chatMessage("There has been another logon under this name, you are being logged out."); + lt.close(); + break; + } + // Second IP Filter check to catch delayed sign-ons + if (game.blnIPF) { + String IP = lt.socket.getInetAddress().toString(); + if (IP.equalsIgnoreCase(socket.getInetAddress().toString())) { + chatMessage("There's already a player connected from your IP address."); + Thread.sleep(1000); + closeNosavePlayer(); + return false; + } + } + } + game.playersByName.put(this.name.toLowerCase(), this); + game.onStart(this); + try (RandomAccessFile rafFile = new RandomAccessFile("users/default", "r")) { + strStore = rafFile.readLine(); + while (!(strStore == null || strStore.equals("."))) { + parseUserFile(rafFile, strStore); + strStore = rafFile.readLine(); + } + } + } catch (Exception e) { + game.log.printError("getPlayer():While loading default user file for " + name, e); + } + strStore = name.toLowerCase(); + File filPlayer = new File("users/" + strStore); + File filBackup = new File("users/" + strStore + ".backup"); + File filCheck; + int i2 = 0; + if (filBackup.exists()) { + if (filPlayer.length() > filBackup.length()) { + filCheck = new File("backup/" + strStore + ".possiblyDamaged"); + while (filCheck.exists()) { + i2++; + filCheck = new File("backup/" + strStore + ".possiblyDamaged." + i2); + } + filBackup.renameTo(filCheck); + } else if (filPlayer.length() < filBackup.length()) { + filCheck = new File("backup/" + strStore + ".possiblyDamaged"); + while (filCheck.exists()) { + i2++; + filCheck = new File("backup/" + strStore + ".possiblyDamaged." + i2); + } + filPlayer.renameTo(filCheck); + filBackup.renameTo(new File("users/" + strStore)); + } + } + /* + ** Load the user + */ + file = new File("users/", strStore); + backup = new File("users/", strStore + ".backup"); + try (RandomAccessFile rafFile = new RandomAccessFile(file, "rw")) { + strStore = rafFile.readLine(); + while (!(strStore == null || strStore.equals("."))) { + parseUserFile(rafFile, strStore); + strStore = rafFile.readLine(); + } + } + /* + ** Try to load a pet, if they don't already have one + */ + if (following == null) { + following = new LivingThing("default", null, this, game); + if (following.name.equalsIgnoreCase("default")) { + following.closeNosavePlayer(); + following = null; + } + } + } catch (Exception e) { + game.log.printError("getPlayer():While loading file for " + name, e); + closeNosavePlayer(); + return true; + } + loadRace(); + isLoaded = true; + if (following != null) { + following.isLoaded = true; + following.changeLocBypass(x, y); + } + return true; + } + + String askRace(String strRaceDir, String prompt) throws IOException { + File filRaces = new File(strRaceDir); + String strList[] = filRaces.list(); + StringBuilder sb = new StringBuilder(prompt); + sb.append("\n"); + for (String s : strList) { + sb.append(s).append("\n"); + } + sb.append(".\n"); + String s = sb.toString(); + + // ?? + if (isPlayer()) { + send(MessageType.ChooseRace, s); + } + if (charmer != null) { + charmer.send(MessageType.ChooseRace, s); + } + if (isPet()) { + return master.instream.readLine().toLowerCase(); + } else { + return instream.readLine().toLowerCase(); + } + } + + public void loadRace() { + String s; + String dir; + if (isPet()) { + dir = "defPets"; + } else { + dir = "defRaces"; + } + try { + if (race == null || !(new File(dir + "/" + race).exists())) { + s = askRace(dir, "Choose one of the following races:"); + // FIXME: check this isn't broken + File filCheck = new File(dir + "/" + s); + while (s.equals("") || !filCheck.exists()) { + s = askRace(dir, "That is not a valid race, please choose again."); + filCheck = new File(dir + "/" + s); + } + race = s; + } + loadRaceFile(new File(dir + "/" + race), true); + } catch (Exception e) { + game.log.printError("loadRace():Loading " + name + "'s race file \"" + dir + "/" + race + "\"", e); + } + } + + public void unloadRace() { + String dir; + if (isPet()) { + dir = "defPets"; + } else { + dir = "defRaces"; + } + try { + if (race == null || !(new File(dir + "/" + race).exists())) { + race = null; + return; + } + loadRaceFile(new File(dir + "/" + race), false); + } catch (Exception e) { + game.log.printError("unloadRace():Un-loading " + name + "'s race file \"" + dir + "/" + race + "\"", e); + } + race = null; + } + + public void updateAppletImages() { + String strResult = "" + (char) 1; + strResult += game.strRCAddress + "\n"; + try { + send(strResult); + } catch (Exception e) { + game.log.printError("updateAppletImages()", e); + } + } + + public void updateApplicationImages() { + String strResult = "" + (char) 1; + strResult += game.strRCName + "\n"; + try { + send(strResult); + } catch (Exception e) { + game.log.printError("updateApplicationImages()", e); + } + } + + public void updateMusic() { + /*try + { + String strResult=""; + String strStore, + strStore2; + int count=0; + RandomAccessFile rafMusic; + + rafMusic = new RandomAccessFile("music0","r"); + strStore = ""; + try + { + strStore2 = rafMusic.readLine(); + while (!(strStore2 == null || strStore2.equals(""))) + { + strStore += strStore2+"\n"; + strStore2 = rafMusic.readLine(); + count++; + } + }catch (Exception e) + { + } + strResult += ""+count+"\n"+strStore; + rafMusic.close(); + rafMusic = new RandomAccessFile("music1","r"); + strStore = ""; + count=0; + try + { + strStore2 = rafMusic.readLine(); + while (!(strStore2 == null || strStore2.equals(""))) + { + strStore += strStore2+"\n"; + strStore2 = rafMusic.readLine(); + count++; + } + }catch (Exception e) + { + } + rafMusic.close(); + strResult += ""+count+"\n"+strStore; + send((char)11+""+2+"\n"+strResult); + }catch(Exception e) + { + engGame.log.printError("updateMusic()", e); + }*/ + } + + public void playMusic(int type) { + /*try + { + send(""+((char)12)+""+type+"\n"); + }catch(Exception e) + { + engGame.log.printError("playMusic()", e); + }*/ + } + + public void playSFX(int intSFX) { + if (audioon) { + try { + send((char) 15 + "" + intSFX + "\n"); + } catch (Exception e) { + game.log.printError("playSFX()", e); + } + } + } + + public void updateActions() { + try { + if (isPlayer()) { + String strResult = "" + (char) 10; + if (battle != null) { + strResult += "flee\n"; + } else { + if (isSleeping) { + strResult += "wake\n"; + } else { + strResult += "sleep\n"; + } + } + strResult += ".\n"; + send(strResult); + } + } catch (Exception e) { + game.log.printError("updateActions()", e); + } + } + + // FIXME: I think this should be encapsulated in a more desriptive message + public void updateEquipment() { + try { + send(MessageType.UpdateEquipment, wornItems.toEntity()); + } catch (Exception e) { + game.log.printError("updateEquipment():" + name + " disconnected", e); + isStopped = true; + return; + } + } + + public void send(MessageType type, String data) { + if (isPlayer() && isWorking && !isClosing) { + // FIXME: put code in senddata + messageQueue.offer(ServerMessage.stringMessage(type, data)); + } + } + + public void send(ServerMessage msg) { + if (isPlayer() && isWorking && !isClosing) { + messageQueue.offer(msg); + } + } + + public void send(String data) { + if (isPlayer() && isWorking && !isClosing) { + messageQueue.offer(ServerMessage.stringMessage(data)); + } + } + + /* public void send(byte data) + { + if (isPlayer() && blnWorking && !blnIsClosing) + { + SendData sd = new SendData(data); + qMessage.push(sd); + } + } + + public void send(long data) + { + if (isPlayer() && blnWorking && !blnIsClosing) + { + SendData sd = new SendData(data); + qMessage.push(sd); + } + }*/ + public void updateInfo() { + try { + String strResult; + strResult = hp + "\n"; + strResult += (maxhp + hpbon) + "\n"; + strResult += mp + "\n"; + strResult += (maxmp + mpbon) + "\n"; + send(MessageType.UpdateStats, strResult); + } catch (Exception e) { + game.log.printError("updateInfo():" + name + " disconnected", e); + isStopped = true; + } + } + + public void updateRange() { + try { + String strResult = "" + (char) 28; + strResult += getRangeWithBonus() + "\n"; + send(strResult); + } catch (Exception e) { + game.log.printError("updateRange():" + name + " disconnected", e); + } + } + + public void updateItems() { + try { + StringBuilder sb = new StringBuilder(); + + for (LinkedList qStore : itemList.values()) { + if (qStore.size() > 0) { + Item itmStore = (Item) qStore.element(); + if (itmStore.isArmor()) { + sb.append((2 + itmStore.intKind) + "\n"); + } else if (itmStore.isWeapon()) { + sb.append("1\n"); + } else { + sb.append("0\n"); + } + sb.append(itmStore.name + "\n"); + } + } + sb.append(".\n"); + send(MessageType.UpdateItems, sb.toString()); + if (game.overMerchant(x, y) != null) { + updateSell(); + } + if (game.overPlayerMerchant(x, y) != null) { + updateSell(); + } + } catch (Exception e) { + game.log.printError("updateItems():" + name + " disconnected", e); + isStopped = true; + } + isSaveNeeded = true; + } + + public void updateStats() { + SpellGroup grpStore; + int i, + i2; + + // FIXME: wtf, duplicated shit code again + // FIXME: convert to stringbuilder + try { + String strResult = ""; + strResult += cash + " gp\n"; + strResult += exp + " exp\n"; + if (strebon == 0) { + strResult += "str: " + stre + "\n"; + } else { + strResult += "str: " + stre + " + " + strebon + "\n"; + } + if (intebon == 0) { + strResult += "int: " + inte + "\n"; + } else { + strResult += "int: " + inte + " + " + intebon + "\n"; + } + if (dextbon == 0) { + strResult += "dex: " + dext + "\n"; + } else { + strResult += "dex: " + dext + " + " + dextbon + "\n"; + } + if (consbon == 0) { + strResult += "con: " + cons + "\n"; + } else { + strResult += "con: " + cons + " + " + consbon + "\n"; + } + if (wisdbon == 0) { + strResult += "wis: " + wisd + "\n"; + } else { + strResult += "wis: " + wisd + " + " + wisdbon + "\n"; + } + if (dammodbon == 0) { + strResult += "DamMod: " + getDamMod() + "\n"; + } else { + strResult += "DamMod: " + getDamMod() + " + " + dammodbon + "\n"; + } + if (acbon == 0) { + strResult += "AC: " + getArmorMod() + "\n\n"; + } else { + strResult += "AC: " + getArmorMod() + " + " + acbon + "\n"; + } + strResult += "-Affected by-\n"; + for (Condition cond : conditions) { + if (cond.display) { + strResult += cond.name + "\n"; + } + } + strResult += "-Skills-\n"; + for (Ability skill : skillMap.values()) { + strResult += skill.name + ": " + skill.getAbility() + "\n"; + } + strResult += "-Spells-\n"; + for (Ability spell : spellMap.values()) { + grpStore = game.getSpellGroup(spell.name); + if (grpStore != null) { + strResult += spell.name + ": " + spell.getAbility() + "\n"; + strResult += grpStore.spellList(spell.getAbility()); + } + } + if (master != null) { + strResult += "\nFollowing: " + master.name + "\n"; + } + if (following != null) { + strResult += "\nFollowed By: " + following.name + "\n"; + if (following.isPet()) { + strResult += following.hp + "/" + following.maxhp + " hp\n"; + strResult += following.mp + "/" + following.maxmp + " mp\n"; + strResult += following.cash + " gp\n"; + strResult += following.exp + " exp\n"; + if (following.strebon == 0) { + strResult += "str: " + following.stre + "\n"; + } else { + strResult += "str: " + following.stre + " + " + following.strebon + "\n"; + } + if (following.intebon == 0) { + strResult += "int: " + following.inte + "\n"; + } else { + strResult += "int: " + following.inte + " + " + following.intebon + "\n"; + } + if (following.dextbon == 0) { + strResult += "dex: " + following.dext + "\n"; + } else { + strResult += "dex: " + following.dext + " + " + following.dextbon + "\n"; + } + if (following.consbon == 0) { + strResult += "con: " + following.cons + "\n"; + } else { + strResult += "con: " + following.cons + " + " + following.consbon + "\n"; + } + if (following.wisdbon == 0) { + strResult += "wis: " + following.wisd + "\n"; + } else { + strResult += "wis: " + following.wisd + " + " + following.wisdbon + "\n"; + } + if (following.dammodbon == 0) { + strResult += "DamMod: " + following.getDamMod() + "\n"; + } else { + strResult += "DamMod: " + following.getDamMod() + " + " + following.dammodbon + "\n"; + } + if (following.acbon == 0) { + strResult += "AC: " + following.getArmorMod() + "\n\n"; + } else { + strResult += "AC: " + following.getArmorMod() + " + " + following.acbon + "\n"; + } + strResult += "-Affected by-\n"; + for (Condition cndStore : following.conditions) { + if (cndStore.display) { + strResult += cndStore.name + "\n"; + } + } + strResult += "-Skills-\n"; + for (Ability skill : following.skillMap.values()) { + strResult += skill.name + ": " + skill.getAbility() + "\n"; + } + strResult += "-Spells-\n"; + for (Ability spell : following.skillMap.values()) { + grpStore = game.getSpellGroup(spell.name); + if (grpStore != null) { + strResult += spell.name + ": " + spell.getAbility() + "\n"; + strResult += grpStore.spellList(spell.getAbility()); + } + } + } + } + strResult += ".\n"; + send(MessageType.UpdateInfo, strResult); + } catch (Exception e) { + game.log.printError("updateStats():" + name + " disconnected", e); + isStopped = true; + } + //updateRange(); + isSaveNeeded = true; + } + + public void halt() { + isHalted = true; + try { + send(MessageType.Halt, ""); + } catch (Exception e) { + isHalted = false; + game.log.printError("halt()", e); + } + } + + public void proceed() { + isHalted = false; + try { + send(MessageType.Proceed, ID + "\n"); + } catch (Exception e) { + game.log.printError("proceed()", e); + } + } + + public void stillThere() { + try { + send("" + (char) 13); + } catch (Exception e) { + game.log.printError("stillThere()", e); + } + } + + public void resizeMap() { + int i, i2; + String strResult = (char) 19 + ""; + strResult += game.mapsize + "\n"; + send(strResult); + } + + public void updateSell() { + StringBuilder sb = new StringBuilder(); + + for (LinkedList list : itemList.values()) { + for (Item item : list) { + sb.append(item.intCost / 2).append("gp)").append(item.name).append("\n"); + } + } + sb.append(".\n"); + send(MessageType.UpdateSell, sb.toString()); + } + + void offMerchant() { + send(MessageType.ExitMerchant, ""); + } + + private class SendThread extends Thread { + + public SendThread() { + } + + public void run() { + ServerMessage msg; + while (!isStopped) { + try { + msg = messageQueue.take(); + try { + msg.send(outstream); + outstream.flush(); + } catch (IOException e) { + game.log.printError("SendThread.run():" + msg + " to " + name, e); + isWorking = false; + isStopped = true; + close(); + } + } catch (InterruptedException ex) { + Logger.getLogger(SendThread.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + } +} diff --git a/DuskServer/src/duskz/server/entity/Merchant.java b/DuskServer/src/duskz/server/entity/Merchant.java new file mode 100644 index 0000000..ca9764c --- /dev/null +++ b/DuskServer/src/duskz/server/entity/Merchant.java @@ -0,0 +1,189 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - modernised java. + */ +package duskz.server.entity; + +import duskz.server.DuskEngine; +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +/** + * Merchant represents a non-mobile entity where players can buy/sell items and + * pets or train. + * + * @author Tom Weingarten + */ +public class Merchant extends DuskObject { + + DuskEngine engGame; + public final List items = new ArrayList<>(); + + public Merchant(DuskEngine inGame) { + super(inGame.getID(), "Merchant"); + engGame = inGame; + } + + @Override + public int getImage() { + return engGame.merchantimage; + } + + @Override + public byte getType() { + return MERCHANT; + } + + @Override + public int getEntityType() { + return 2; + } + + public boolean contains(String item) { + return items.contains(item); + } + + public void remove(String item) { + items.remove(item); + } + + public void pet(LivingThing thnMaster) { + try { + if (thnMaster.following != null && thnMaster.following.isPet()) { + thnMaster.chatMessage("You are only allowed 1 pet."); + return; + } + if (thnMaster.cash >= engGame.petcost) { + thnMaster.cash -= engGame.petcost; + } else { + thnMaster.chatMessage("You can't afford a pet."); + return; + } + if (thnMaster.isPet()) { + thnMaster.chatMessage("You ARE a pet!"); + return; + } + thnMaster.halt(); + thnMaster.chatMessage("Enter a name for your pet:"); + String strName = thnMaster.instream.readLine().trim(); + File filCheck = new File("pets/" + strName.toLowerCase()); + File filCheck2 = new File("users/" + strName.toLowerCase()); + while (filCheck.exists() || filCheck2.exists() + || !engGame.isGoodName(strName)) { + thnMaster.chatMessage("That name is already taken or has invalid characters, please enter another:"); + strName = thnMaster.instream.readLine().trim(); + filCheck = new File("pets/" + strName.toLowerCase()); + filCheck2 = new File("users/" + strName.toLowerCase()); + } + thnMaster.chatMessage("Choose a type of pet:"); + File filPetTypes = new File("defPets"); + String strList[] = filPetTypes.list(); + for (int i = 0; i < strList.length; i++) { + thnMaster.chatMessage(strList[i]); + } + String strStore = thnMaster.instream.readLine().trim(); + filCheck = new File("defPets/" + strStore); + while (strStore.equals("") || !filCheck.exists()) { + thnMaster.chatMessage("That is not a valid pet type:"); + strStore = thnMaster.instream.readLine().trim(); + filCheck = new File("defPets/" + strStore); + } + thnMaster.following = new LivingThing(strName, strStore, thnMaster, engGame); + } catch (Exception e) { + engGame.log.printError("Merchant.pet()", e); + } finally { + thnMaster.proceed(); + } + } + + public void train(String how, int quantity, LivingThing lt) { + quantity = Math.min(quantity, 100); + + if (lt.exp < engGame.traincost * quantity) { + lt.chatMessage("Sorry, you don't have enough experience points."); + return; + } + + switch (how.toLowerCase()) { + case "hp": + lt.exp -= engGame.traincost * quantity; + lt.maxhp += 10 * quantity; + lt.hp += 10 * quantity; + lt.updateInfo(); + break; + case "mp": + lt.exp -= engGame.traincost * quantity; + lt.maxmp += 10 * quantity; + lt.mp += 10 * quantity; + lt.updateInfo(); + break; + case "strength": + if (lt.stre + quantity > lt.stre_limit) { + quantity = lt.stre_limit - lt.stre; + } + lt.exp -= engGame.traincost * quantity; + lt.stre += quantity; + break; + case "intelligence": + if (lt.inte + quantity > lt.inte_limit) { + quantity = lt.inte_limit - lt.inte; + } + lt.exp -= engGame.traincost * quantity; + lt.inte += quantity; + break; + case "wisdom": + if (lt.wisd + quantity > lt.wisd_limit) { + quantity = lt.wisd_limit - lt.wisd; + } + lt.exp -= engGame.traincost * quantity; + lt.wisd += quantity; + break; + case "constitution": + if (lt.cons + quantity > lt.cons_limit) { + quantity = lt.cons_limit - lt.cons; + } + lt.exp -= engGame.traincost * quantity; + lt.cons += quantity; + break; + case "dexterity": + if (lt.dext + quantity > lt.dext_limit) { + quantity = lt.dext_limit - lt.dext; + } + lt.exp -= engGame.traincost * quantity; + lt.dext += quantity; + break; + default: + int skillLeft = 100 - lt.getSkill(how); + if (quantity > skillLeft) { + quantity = skillLeft; + } + if (lt.addToSkill(how, (byte) quantity)) { + lt.exp -= quantity * engGame.traincost; + } else { + return; + } + } + lt.updateStats(); + } +} diff --git a/DuskServer/src/duskz/server/entity/Mob.java b/DuskServer/src/duskz/server/entity/Mob.java new file mode 100644 index 0000000..b053714 --- /dev/null +++ b/DuskServer/src/duskz/server/entity/Mob.java @@ -0,0 +1,267 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - modernised java, script wrapper, + * loader function. + */ +package duskz.server.entity; + +import duskz.server.Condition; +import duskz.server.DuskEngine; +import duskz.server.Faction; +import duskz.server.GiveItem; +import duskz.server.ItemList; +import duskz.server.Log; +import duskz.server.Script; +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +/** + * Mob(mobile) is the class of non-player moving entities. + * + * @author Tom Weingarten + */ +public class Mob extends LivingThing { + + final public List vctGiveItems = new ArrayList<>(); + //Mob level + public int level = -1; + //AI + public double dblBravery = 0, //0 to 1, -1 makes mob always flee + dblGroupRelation = 0; //-1 to 1 + public String strOnBattle = null; + //Faction + public Faction fctFaction; + public boolean blnOneUse = false; + public boolean blnCanSeePlayer = false; + + //new Mob + public Mob(String strType, int locx, int locy, DuskEngine inEngine) + throws Exception { + super(inEngine.getID(), strType); + noChannel = -1; + game = inEngine; + Type = 1; + race = strType; + x = locx; + y = locy; + originalX = x; + originalY = y; + wornItems = new Equipment(); + itemList = new ItemList(); + battle = null; + loadMobFile(new File("defMobs/" + strType)); + hp = maxhp; + mp = maxmp; + isSleeping = false; + isLoaded = true; + } + + //new Mob w/ levels (provided for pre-2.3 compatibility) + public Mob(String strType, int level, int locx, int locy, DuskEngine inEngine) + throws Exception { + super(inEngine.getID(), strType); + noChannel = -1; + game = inEngine; + Type = 1; + race = strType; + x = locx; + y = locy; + originalX = x; + originalY = y; + wornItems = new Equipment(); + itemList = new ItemList(); + battle = null; + this.level = level; + loadMobFile(new File("defMobs/" + strType)); + maxhp = maxhp * level; + maxmp = maxmp * level; + stre = stre * level; + inte = inte * level; + wisd = wisd * level; + dext = dext * level; + cons = cons * level; + hp = maxhp * level; + mp = maxmp * level; + isSleeping = false; + isLoaded = true; + } + + @Override + public void leaveBattle() { + super.leaveBattle(); + if (blnOneUse) { + game.removeDuskObject(this); + } else { + game.mobKilled(this); + // FIXME: need a game.mobKilled() function + // FIXME: this magic number is used in TickThread and DuskEngine + x = -6; + y = -6; + hp = game.mobrespawnspeed;//seconds till respawn + } + } + + public void loadMobFile(File path) throws FileNotFoundException, IOException { + String line; + try (RandomAccessFile file = new RandomAccessFile(path, "r")) { + while (!((line = file.readLine()) == null || line.equals("."))) { + try { + parseMobFile(file, line); + } catch (NumberFormatException x) { + throw new IOException("Problem parsing mob " + path + " on field " + line, x); + } + } + if (fctFaction == null) { + game.log.printMessage(Log.DEBUG, "no faction found for mob \"" + name + "\""); + } + } + } + + private void parseMobFile(RandomAccessFile in, String strStore) throws IOException, NumberFormatException { + switch (strStore.toLowerCase()) { + case "skill": + strStore = in.readLine(); + int value = Byte.parseByte(in.readLine()); + addToSkill(strStore, value); + game.log.printMessage(Log.DEBUG, strStore + "=" + value); + break; + case "condition": + Condition cndStore = game.getCondition(in.readLine()); + cndStore.ticksPast = Integer.parseInt(in.readLine()); + cndStore.duration = Integer.parseInt(in.readLine()); + addCondition(cndStore); + game.log.printMessage(Log.DEBUG, "condition \"" + cndStore.name + "\""); + break; + case "giveitem": + String strItem = in.readLine(); + double dblChance = Double.valueOf(in.readLine()).doubleValue(); + vctGiveItems.add(new GiveItem(strItem, dblChance)); + game.log.printMessage(Log.DEBUG, name + " gives a \"" + strItem + "\" " + (100 * dblChance) + "% of the time."); + break; + case "item": + Item itmStore = game.getItem(in.readLine()); + if (itmStore != null) { + itmStore.lngDurability = Long.parseLong(in.readLine()); + itmStore.intUses = Integer.parseInt(in.readLine()); + itemList.addElement(itmStore); + } + break; + case "clan": + clan = in.readLine(); + break; + case "race": + race = in.readLine(); + break; + case "title": + title = in.readLine(); + break; + case "description": + description = in.readLine(); + break; + case "x": + x = Integer.parseInt(in.readLine()); + break; + case "y": + y = Integer.parseInt(in.readLine()); + break; + case "maxhp": + maxhp = Integer.parseInt(in.readLine()); + break; + case "maxmp": + maxmp = Integer.parseInt(in.readLine()); + break; + case "stre": + stre = Integer.parseInt(in.readLine()); + break; + case "inte": + inte = Integer.parseInt(in.readLine()); + break; + case "dext": + dext = Integer.parseInt(in.readLine()); + break; + case "cons": + cons = Integer.parseInt(in.readLine()); + break; + case "wisd": + wisd = Integer.parseInt(in.readLine()); + break; + case "image": + imageid = Integer.parseInt(in.readLine()); + break; + case "bravery": + dblBravery = Double.valueOf(in.readLine()).doubleValue(); + break; + case "grouprelation": + dblGroupRelation = Double.valueOf(in.readLine()).doubleValue(); + break; + case "wield": + parseWear(in, Equipment.WIELD); + break; + case "arms": + parseWear(in, Equipment.ARMS); + break; + case "legs": + parseWear(in, Equipment.LEGS); + break; + case "torso": + parseWear(in, Equipment.TORSO); + break; + case "waist": + parseWear(in, Equipment.WAIST); + break; + case "neck": + parseWear(in, Equipment.NECK); + break; + case "skull": + parseWear(in, Equipment.SKULL); + break; + case "eyes": + parseWear(in, Equipment.EYES); + break; + case "hands": + parseWear(in, Equipment.HANDS); + break; + case "faction": + String strFaction = in.readLine(); + fctFaction = game.getFaction(strFaction); + if (fctFaction != null) { + game.log.printMessage(Log.DEBUG, "faction=\"" + fctFaction.strName + "\""); + } else { + game.log.printMessage(Log.DEBUG, "no faction found for \"" + strFaction + "\""); + } + break; + case "onBattle": + strOnBattle = in.readLine(); + break; + case "nofollow": + noFollow = true; + break; + } + // engGame.log.printError("parseMobFile():Parsing \"" + strStore + "\" from " + name + "'s file", e); + } + + public void onBattle(DuskEngine engGame) { + Script.exec("scripts/" + strOnBattle, engGame, this); + } +} \ No newline at end of file diff --git a/DuskServer/src/duskz/server/entity/PlayerMerchant.java b/DuskServer/src/duskz/server/entity/PlayerMerchant.java new file mode 100644 index 0000000..3006db6 --- /dev/null +++ b/DuskServer/src/duskz/server/entity/PlayerMerchant.java @@ -0,0 +1,78 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.server.entity; + +import duskz.server.DuskEngine; +import duskz.server.ItemList; + +/** + * Merchant represents a non-mobile entity where This merchant holds/sells items + * for the player that owns it The owner can give and take items and gp from it. + * The merchant will only be able to sell items it has been given. And only the + * number it has been given. + * + * @author Wildern + */ +public class PlayerMerchant extends DuskObject { + + DuskEngine engGame; + public ItemList vctItems; + // FIXME: not public!! + public long cash; + public String strOwner; + + public PlayerMerchant(DuskEngine inGame) { + super(inGame.getID(), "Merchant"); + engGame = inGame; + } + + @Override + public int getImage() { + return engGame.merchantimage; + } + + @Override + public byte getType() { + return PLAYER_MARCHANT; + } + + @Override + public int getEntityType() { + return 2; + } + + public long contains(String strStore) { + if (vctItems.contains(strStore)) { + return vctItems.get(strStore).size(); + } + return 0; + } + + public void add(Item itmStore) { + vctItems.addElement(itmStore); + } + + public Item remove(String strStore) { + return vctItems.removeElement(strStore); + } +} diff --git a/DuskServer/src/duskz/server/entity/Prop.java b/DuskServer/src/duskz/server/entity/Prop.java new file mode 100644 index 0000000..da5cdb0 --- /dev/null +++ b/DuskServer/src/duskz/server/entity/Prop.java @@ -0,0 +1,53 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.server.entity; + +/** + * A Prop is an entity that cannot move or be used. + * + * @author Tom Weingarten + */ +public class Prop extends DuskObject { + + public int intImage; + + public Prop(long lngID, String name) { + super(lngID, name); + } + + @Override + public int getImage() { + return intImage; + } + + @Override + public byte getType() { + return PROP; + } + + @Override + public int getEntityType() { + return 3; + } +} diff --git a/DuskServer/src/duskz/server/entity/Sign.java b/DuskServer/src/duskz/server/entity/Sign.java new file mode 100644 index 0000000..8753a99 --- /dev/null +++ b/DuskServer/src/duskz/server/entity/Sign.java @@ -0,0 +1,60 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.server.entity; + +import duskz.server.DuskEngine; + +/** + * Represents a sign object. A sign contains a string message and can be viewed + * by LivingThings. + * + * @author Tom Weingarten + */ +public class Sign extends DuskObject { + + public String strMessage; + private final DuskEngine eng; + + public Sign(DuskEngine eng, String name, String msg, int locx, int locy, long lngID) { + super(lngID, name); + strMessage = msg; + x = locx; + y = locy; + this.eng = eng; + } + + @Override + public byte getType() { + return SIGN; + } + + @Override + public int getImage() { + return eng.signimage; + } + + @Override + public int getEntityType() { + return 3; + } +} diff --git a/DuskServer/src/duskz/server/entity/TileMap.java b/DuskServer/src/duskz/server/entity/TileMap.java new file mode 100644 index 0000000..7d9d6cc --- /dev/null +++ b/DuskServer/src/duskz/server/entity/TileMap.java @@ -0,0 +1,740 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + */ +package duskz.server.entity; + +import duskz.util.Maths; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.PriorityQueue; + +/** + * Low level map management and helpers + * + * @author notzed + */ +public class TileMap implements Iterable { + + private int rows, cols; + /** + * Tile map + */ + private short tiles[]; + /** + * Entities, they are chained off each other for efficiency reasons + */ + private DuskObject entities[]; + /** + * privileges for each cell. This appears to be unimplemented in Dusk so + * isn't here either. + */ + protected short privs[]; + /** + * Ownership for each cell. This appears to be unimplemented in Dusk so + * isn't here either. + */ + protected int owner[]; + /** + * Flags for iterators + */ + public final static int SKIP_START = 1; + public final static int SKIP_END = 2; + + /** + * Create a new empty map of the given size + * + * @param cols + * @param rows + */ + public TileMap(int cols, int rows) { + this.rows = rows; + this.cols = cols; + + tiles = new short[rows * cols]; + entities = new DuskObject[rows * cols]; + } + + public int getRows() { + return rows; + } + + public int getCols() { + return cols; + } + + public void saveMap(File path) throws IOException { + path.delete(); + try (RandomAccessFile rafFile = new RandomAccessFile(path, "rw")) { + rafFile.writeInt(cols); + rafFile.writeInt(rows); + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { + rafFile.writeShort(getTile(x, y)); + } + } + } + } + public static final int FORMAT_BYTE = 0; + public static final int FORMAT_SHORT = 1; + + // Map in row major format (i.e. more efficient) + public static TileMap loadMapX(File path) throws IOException { + TileMap map; + + try (DataInputStream mapFile = new DataInputStream(new FileInputStream(path))) { + int cols = mapFile.readInt(); + int rows = mapFile.readInt(); + map = new TileMap(cols, rows); + for (int y = 0; y < rows; y++) { + for (int x = 0; x < cols; x++) { + map.setTile(x, y, mapFile.readShort()); + } + } + } + return map; + } + + public static TileMap loadMap(File path, int format) throws IOException { + TileMap map; + + try (RandomAccessFile mapFile = new RandomAccessFile(path, "r")) { + int cols = mapFile.readInt(); + int rows = mapFile.readInt(); + map = new TileMap(cols, rows); + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { + if (format == FORMAT_BYTE) + map.setTile(x, y, mapFile.readByte()); + else + map.setTile(x, y, mapFile.readShort()); + } + } + } + return map; + } + + /** + * Create a new map + * + * @param newcols + * @param newrows + * @return + */ + public synchronized void resize(int newcols, int newrows) { + short[] ntiles = new short[newcols * newrows]; + DuskObject[] nentities = new DuskObject[newcols * newrows]; + + int rx = Math.min(newcols, cols); + int ry = Math.min(newrows, rows); + + for (int y = 0; y < ry; y++) { + for (int x = 0; x < rx; x++) { + int indexa = x + y * cols; + int indexb = x + y * newcols; + + ntiles[indexb] = tiles[indexa]; + nentities[indexb] = entities[indexa]; + } + } + + tiles = ntiles; + entities = nentities; + cols = newcols; + rows = newrows; + } + + public boolean inside(int x, int y) { + return x >= 0 && x < cols + && y >= 0 && y < rows; + } + + public void setTile(int x, int y, int t) { + tiles[x + y * cols] = (short) t; + } + + public short getTile(int x, int y) { + return tiles[x + y * cols]; + } + + public synchronized List getEntities(int x, int y, List list) { + if (list == null) + list = new ArrayList<>(); + + DuskObject o = entities[x + y * cols]; + while (o != null) { + list.add(o); + o = o.getNext(); + } + return list; + } + + public synchronized void addEntity(DuskObject o) { + if (inside(o.x, o.y)) { + int index = o.x + o.y * cols; + + entities[index] = DuskObject.append(entities[index], o); + } + } + + public synchronized void removeEntity(DuskObject o) { + if (inside(o.x, o.y)) { + int index = o.x + o.y * cols; + + entities[index] = DuskObject.remove(entities[index], o); + } + } + + /** + * Get an iterable over a range - allows foreach support + * + * @param x0 + * @param y0 + * @param x1 + * @param y1 + * @return + */ + public Iterable range(int x0, int y0, int x1, int y1) { + return new MapIterable(x0, y0, x1, y1); + } + + /** + * Get an iterable over a range with a given centre and radius + * + * @param x + * @param y + * @param radius + * @return + */ + public Iterable range(int x, int y, int radius) { + return new MapIterable(x - radius, y - radius, x + radius + 1, y + radius + 1); + } + + /** + * Get an iterable which will iterate over the looking path + * + * @param sx + * @param sy + * @param ex + * @param ey + * @param flags SKIP_END, SKIP_START to skip end/start locations + * (UNIMPLEMENTED) + * @return + */ + public Iterable look(int sx, int sy, int ex, int ey, int flags) { + return new LookIterable(sx, sy, ex, ey, flags); + } + + public Iterable look(int sx, int sy, int ex, int ey) { + return new LookIterable(sx, sy, ex, ey, 0); + } + + public Iterable move(int sx, int sy, int ex, int ey, int flags, MoveListener l) { + return new MoveIterable(sx, sy, ex, ey, flags, l); + } + + private class MapIterable implements Iterable { + + int x0, y0, x1, y1; + + public MapIterable(int x0, int y0, int x1, int y1) { + this.x0 = x0; + this.y0 = y0; + this.x1 = x1; + this.y1 = y1; + } + + @Override + public Iterator iterator() { + return new MapIterator(x0, y0, x1, y1); + } + } + + private class LookIterable implements Iterable { + + int sx, sy, ex, ey; + int flags; + + public LookIterable(int sx, int sy, int ex, int ey, int flags) { + this.sx = sx; + this.sy = sy; + this.ex = ex; + this.ey = ey; + this.flags = flags; + } + + @Override + public Iterator iterator() { + return new LookIterator(sx, sy, ex, ey, flags); + } + } + + private class MoveIterable implements Iterable { + + int sx, sy, ex, ey; + int flags; + MoveListener l; + + public MoveIterable(int sx, int sy, int ex, int ey, int flags, MoveListener l) { + this.sx = sx; + this.sy = sy; + this.ex = ex; + this.ey = ey; + this.flags = flags; + this.l = l; + } + + @Override + public Iterator iterator() { + return new MoveIterator(sx, sy, ex, ey, flags, l); + } + } + + public Iterator getIterator(int x0, int y0, int x1, int y1) { + return new MapIterator(x0, y0, x1, y1); + } + + @Override + public Iterator iterator() { + return getIterator(0, 0, cols, rows); + } + + public class MapData { + + public int x, y; + public final List entities = new ArrayList<>(); + public short tile; + + protected void setData(int x, int y) { + this.x = x; + this.y = y; + this.entities.clear(); + if (inside(x, y)) { + this.tile = getTile(x, y); + getEntities(x, y, this.entities); + } else { + this.tile = 0; + } + } + } + + public class MoveData extends MapData { + + public String direction; + } + + private class MapIterator implements Iterator { + + int x0, y0, x1, y1; + int x, y; + MapData data = new MapData(); + + public MapIterator(int x0, int y0, int x1, int y1) { + this.x0 = Maths.clamp(x0, 0, cols); + this.y0 = Maths.clamp(y0, 0, rows); + this.x1 = Maths.clamp(x1, 0, cols); + this.y1 = Maths.clamp(y1, 0, rows); + this.x = -1; + this.y = -1; + } + + @Override + public boolean hasNext() { + return (x + 1) < x1 + || (y + 1) < y1; + } + + @Override + public MapData next() { + // FIXME: this needs to iterate in x first then y + if (x == -1) { + x = x0; + y = y0; + } else if (y + 1 < y1) { + y++; + } else { + y = y0; + x++; + } + + data.setData(x, y); + + return data; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported yet."); + } + } + + /** + * Implements an iterator which follows a 'looking' path + * + * TODO: it should probably use Bresenhams line algorithm + */ + private class LookIterator implements Iterator { + + int x, y; + final int sx, sy; + final int ex, ey; + int flags; + boolean there = false; + MapData data = new MapData(); + + public LookIterator(int sx, int sy, int ex, int ey, int flags) { + this.x = sx; + this.y = sy; + this.sx = sx; + this.sy = sy; + this.ex = ex; + this.ey = ey; + this.flags = flags; + + diffx = Math.abs(ex - sx); + diffy = Math.abs(ey - sy); + stepx = Integer.signum(ex - sx); + stepy = Integer.signum(ey - sy); + err = diffx - diffy; + } + + @Override + public boolean hasNext() { + return !there; + } + // Bresenham algorithm data, from wikipedia + int diffx, diffy; + int stepx, stepy; + int err; + + void lineStep() { + int e2 = 2 * err; + if (e2 > -diffy) { + err -= diffy; + x += stepx; + } + if (e2 < diffx) { + err += diffx; + y += stepy; + } + } + + @Override + public MapData next() { + there = x == ex && y == ey; + + // FIXME: impelement? + //if ((flags & SKIP_START) != 0 && sx == x && sy == y) { + // lineStep(); + //} + + data.setData(x, y); + + if (!there) { + lineStep(); + } + return data; + } + + public MapData nextOld() { + there = x == ex && y == ey; + + data.setData(x, y); + + if (!there) { + int stepx = Integer.signum(ex - y); + int stepy = Integer.signum(ey - y); + int dx = Math.abs(ex - x); + int dy = Math.abs(ey - y); + + if (dx > dy) { + x += stepx; + } else if (dx < dy) { + y += stepy; + } else { + x += stepx; + y += stepy; + } + } + + return data; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported yet."); + } + } + + public interface MoveListener { + + public boolean canMoveto(MapData md); + } + + /** + * Iterator for movement. + */ + private static class MoveInfo implements Comparable { + + int x, y; + float cost; + float estimate; + String direction; + MoveInfo parent; + + public MoveInfo(int x, int y, float cost, String direction) { + this.x = x; + this.y = y; + this.cost = cost; + this.direction = direction; + } + + @Override + public boolean equals(Object obj) { + MoveInfo o = (MoveInfo) obj; + + return x == o.x && y == o.y; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 59 * hash + this.x; + hash = 59 * hash + this.y; + return hash; + } + + @Override + public int compareTo(MoveInfo o) { + return Float.compare(cost, o.cost); + } + } + + /** + * An iterator which steps through the individual moves to get to a + * destination. + * + * Implemented using A* algorithm, so can handle obstructions. + * + * TODO: Limit the search space. + */ + private class MoveIterator implements Iterator { + + int x, y; + final int sx, sy; + final int ex, ey; + int flags; + boolean there = false; + private final MoveListener l; + // quick hack version + Iterator iterator; + MoveData data = new MoveData(); + + public MoveIterator(int sx, int sy, int ex, int ey, int flags, MoveListener l) { + this.x = sx; + this.y = sy; + this.sx = sx; + this.sy = sy; + this.ex = ex; + this.ey = ey; + this.flags = flags; + this.l = l; + + // A* requires the whole path to be calculated ahead of time. + + List path = findPath(); + + System.out.printf("Finding path from %d,%d to %d,%d: ", sx, sy, ex, ey); + + if (path != null) { + for (MoveInfo mi : path) { + System.out.print(" "); + System.out.print(mi.direction); + } + System.out.println(); + + if (!path.isEmpty() && (flags & SKIP_END) != 0) { + path.remove(path.size() - 1); + } + + iterator = path.iterator(); + } else { + System.out.println("No path found!"); + } + } + + float estimateCost(int sx, int sy, int ex, int ey) { + return (float) Math.sqrt((ex - sx) * (ex - sx) + (ey - sy) * (ey - sy)); + } + + List constructPath(List list, MoveInfo n) { + if (list == null) + list = new ArrayList<>(); + + if (n.parent != null) { + constructPath(list, n.parent); + list.add(n); + } + + return list; + } + + void moveIf(List list, int x, int y, float cost, String dir) { + data.setData(x, y); + + if (l.canMoveto(data)) { + list.add(new MoveInfo(x, y, cost + 1, dir)); + } + } + + List getNeighbours(MoveInfo n) { + List list = new ArrayList<>(4); + + moveIf(list, n.x + 1, n.y, n.cost, "e"); + moveIf(list, n.x - 1, n.y, n.cost, "w"); + moveIf(list, n.x, n.y + 1, n.cost, "s"); + moveIf(list, n.x, n.y - 1, n.cost, "n"); + + return list; + } + + // A* algorithm from here: + //http://www.peachpit.com/articles/article.aspx?p=101142&seqNum=2 + public List findPath() { + + PriorityQueue openList = new PriorityQueue(); + HashSet closedList = new HashSet<>(); + + MoveInfo startNode = new MoveInfo(sx, sy, 0, null); + startNode.estimate = estimateCost(sx, sy, ex, ey); + startNode.parent = null; + openList.add(startNode); + + while (!openList.isEmpty()) { + MoveInfo node = openList.poll(); + if (node.x == ex && node.y == ey) { + // construct the path from start to goal + return constructPath(null, node); + } + + List neighbors = getNeighbours(node); + for (int i = 0; i < neighbors.size(); i++) { + MoveInfo nnode = neighbors.get(i); + boolean isOpen = openList.contains(nnode); + boolean isClosed = closedList.contains(nnode); + float costFromStart = node.cost + 1; + + // check if the neighbor node has not been + // traversed or if a shorter path to this + // neighbor node is found. + if ((!isOpen && !isClosed) + || costFromStart < nnode.cost) { + nnode.parent = node; + nnode.cost = costFromStart; + nnode.estimate = estimateCost(nnode.x, nnode.y, ex, ey); + if (isClosed) { + closedList.remove(nnode); + } + if (!isOpen) { + openList.add(nnode); + } + } + } + closedList.add(node); + } + + // no path found + return null; + } + + @Override + public boolean hasNext() { + if (iterator != null) + return iterator.hasNext(); + else + return false; + } + + @Override + public MoveData next() { + MoveInfo mi = iterator.next(); + + data.setData(mi.x, mi.y); + data.direction = mi.direction; + + return data; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } + + public static void main(String[] args) { + TileMap map = new TileMap(16, 16); + + /* + for (MapData md : map.look(5, 5, 10, 3)) { + System.out.printf(" %d,%d\n", md.x, md.y); + } + for (MapData md : map.look(15, 15, 0, 0)) { + System.out.printf(" %d,%d\n", md.x, md.y); + } + for (MapData md : map.look(15, 0, 0, 15)) { + System.out.printf(" %d,%d\n", md.x, md.y); + }*/ + + + MoveListener l = new MoveListener() { + @Override + public boolean canMoveto(MapData md) { + //System.out.printf("can move %d,%d\n", md.x, md.y); + return md.tile == 0; + } + }; + System.out.println("no obstacles"); + for (MoveData md : map.move(0, 0, 10, 10, 0, l)) { + System.out.printf(" %d,%d %s\n", md.x, md.y, md.direction); + } + + // put an obstacle in the way + for (int x = 0; x < 11; x++) { + map.setTile(x, 5, 1); + } + System.out.println("line in the way"); + for (MoveData md : map.move(0, 0, 10, 10, SKIP_END, l)) { + System.out.printf(" %d,%d %s\n", md.x, md.y, md.direction); + } + System.out.println("line in the way to last"); + for (MoveData md : map.move(0, 0, 1, 1, 0, l)) { + System.out.printf(" %d,%d %s\n", md.x, md.y, md.direction); + } + + } +} diff --git a/DuskZ/COPYING b/DuskZ/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/DuskZ/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/DuskZ/COPYING.GPLv2 b/DuskZ/COPYING.GPLv2 new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/DuskZ/COPYING.GPLv2 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/DuskZ/README b/DuskZ/README new file mode 100644 index 0000000..5ab7274 --- /dev/null +++ b/DuskZ/README @@ -0,0 +1,44 @@ + +README +------ +This is the client frontend to DuskZ, it uses JavaFX. It connects +to the DuskServer togehter and requires some local data installed. + +It is a fork and major overhaul of the Dusk 2.7.3 source code, released +circa 2000. + +Currently a recent Oracle JRE is required to execute this application. + +This is currently in an alpha state and in very active development. + + ... to be completed ... + +INSTALLATION +------------ + ... to be completed ... + +RUNNING +------- + ... to be completed ... + +LICENSE +------- + DuskZ is free software, see COPYING for your rights. + + Some files are under other compatible licenses. + + Copyright (C) 2000 Tom Weingarten + Copyright (C) 2013 Michael Zucchi + + DuskZ is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DuskZ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DuskZ. If not, see . diff --git a/DuskZ/build.xml b/DuskZ/build.xml new file mode 100644 index 0000000..0d44bda --- /dev/null +++ b/DuskZ/build.xml @@ -0,0 +1,53 @@ + + Builds, tests, and runs the project DuskZ. + + + diff --git a/DuskZ/manifest.mf b/DuskZ/manifest.mf new file mode 100644 index 0000000..328e8e5 --- /dev/null +++ b/DuskZ/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/DuskZ/nbproject/build-impl.xml b/DuskZ/nbproject/build-impl.xml new file mode 100644 index 0000000..467fa6e --- /dev/null +++ b/DuskZ/nbproject/build-impl.xml @@ -0,0 +1,1459 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set platform.home + Must set platform.bootcp + Must set platform.java + Must set platform.javac + + The J2SE Platform is not correctly set up. + Your active platform is: ${platform.active}, but the corresponding property "platforms.${platform.active}.home" is not found in the project's properties files. + Either open the project in the IDE and setup the Platform with the same name or add it manually. + For example like this: + ant -Duser.properties.file=<path_to_property_file> jar (where you put the property "platforms.${platform.active}.home" in a .properties file) + or ant -Dplatforms.${platform.active}.home=<path_to_JDK_home> jar (where no properties file is used) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + ${platform.java} -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + ${platform.java} -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DuskZ/nbproject/configs/Run_as_WebStart.properties b/DuskZ/nbproject/configs/Run_as_WebStart.properties new file mode 100644 index 0000000..670fff0 --- /dev/null +++ b/DuskZ/nbproject/configs/Run_as_WebStart.properties @@ -0,0 +1,2 @@ +# Do not modify this property in this configuration. It can be re-generated. +$label=Run as WebStart diff --git a/DuskZ/nbproject/configs/Run_in_Browser.properties b/DuskZ/nbproject/configs/Run_in_Browser.properties new file mode 100644 index 0000000..f2a5a65 --- /dev/null +++ b/DuskZ/nbproject/configs/Run_in_Browser.properties @@ -0,0 +1,2 @@ +# Do not modify this property in this configuration. It can be re-generated. +$label=Run in Browser diff --git a/DuskZ/nbproject/genfiles.properties b/DuskZ/nbproject/genfiles.properties new file mode 100644 index 0000000..a6ee1b3 --- /dev/null +++ b/DuskZ/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=be5f771c +build.xml.script.CRC32=d9af4be0 +build.xml.stylesheet.CRC32=28e38971@1.56.1.46 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=46d0094e +nbproject/build-impl.xml.script.CRC32=21080c51 +nbproject/build-impl.xml.stylesheet.CRC32=c6d2a60f@1.56.1.46 diff --git a/DuskZ/nbproject/jfx-impl.xml b/DuskZ/nbproject/jfx-impl.xml new file mode 100644 index 0000000..43adba7 --- /dev/null +++ b/DuskZ/nbproject/jfx-impl.xml @@ -0,0 +1,3218 @@ + + + + + JavaFX-specific Ant calls + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${cssfileslist} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DuskZ/nbproject/project.properties b/DuskZ/nbproject/project.properties new file mode 100644 index 0000000..0c0a31d --- /dev/null +++ b/DuskZ/nbproject/project.properties @@ -0,0 +1,120 @@ +project.license=dusk3 +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=DuskZ +application.vendor=notzed +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +compile.on.save=true +compile.on.save.unsupported.javafx=true +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/DuskZ.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +includes=** +# Non-JavaFX jar file creation is deactivated in JavaFX 2.0+ projects +jar.archive.disabled=true +jar.compress=false +javac.classpath=\ + ${javafx.runtime}/lib/jfxrt.jar:\ + ${javafx.runtime}/lib/deploy.jar:\ + ${javafx.runtime}/lib/javaws.jar:\ + ${javafx.runtime}/lib/plugin.jar:\ + ${reference.DuskCommon.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +javafx.binarycss=false +javafx.deploy.adddesktopshortcut=false +javafx.deploy.addstartmenushortcut=false +javafx.deploy.allowoffline=true +# If true, application update mode is set to 'background', if false, update mode is set to 'eager' +javafx.deploy.backgroundupdate=false +javafx.deploy.embedJNLP=true +javafx.deploy.includeDT=true +javafx.deploy.installpermanently=false +javafx.deploy.permissionselevated=false +# Set true to prevent creation of temporary copy of deployment artifacts before each run (disables concurrent runs) +javafx.disable.concurrent.runs=false +# Set true to enable multiple concurrent runs of the same WebStart or Run-in-Browser project +javafx.enable.concurrent.external.runs=false +# This is a JavaFX project +javafx.enabled=true +javafx.fallback.class=com.javafx.main.NoJavaFXFallback +# Main class for JavaFX +javafx.main.class=duskz.client.fx.DuskFX +javafx.native.bundling.enabled=false +javafx.native.bundling.type=none +javafx.preloader.class= +# This project does not use Preloader +javafx.preloader.enabled=false +javafx.preloader.jar.filename= +javafx.preloader.jar.path= +javafx.preloader.project.path= +javafx.preloader.type=none +# Set true for GlassFish only. Rebases manifest classpaths of JARs in lib dir. Not usable with signed JARs. +javafx.rebase.libs=false +javafx.run.height=600 +javafx.run.width=800 +javafx.runtime=${platforms.Default_JavaFX_Platform.javafx.runtime.home} +javafx.sdk=${platforms.Default_JavaFX_Platform.javafx.sdk.home} +javafx.signing.enabled=false +javafx.signing.type=notsigned +# Pre-JavaFX 2.0 WebStart is deactivated in JavaFX 2.0+ projects +jnlp.enabled=false +# Main class for Java launcher +main.class=com.javafx.main.Main +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=Default_JavaFX_Platform +project.DuskCommon=../DuskCommon +reference.DuskCommon.jar=${project.DuskCommon}/dist/DuskCommon.jar +run.classpath=\ + ${dist.jar}:\ + ${javac.classpath} +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/DuskZ/nbproject/project.xml b/DuskZ/nbproject/project.xml new file mode 100644 index 0000000..7782e6a --- /dev/null +++ b/DuskZ/nbproject/project.xml @@ -0,0 +1,39 @@ + + + org.netbeans.modules.java.j2seproject + + + + + + + + + + + + + + + + DuskZ + + + + + + + + + + + DuskCommon + jar + + jar + clean + jar + + + + diff --git a/DuskZ/src/duskz/client/Bookmarks.java b/DuskZ/src/duskz/client/Bookmarks.java new file mode 100644 index 0000000..36e54f6 --- /dev/null +++ b/DuskZ/src/duskz/client/Bookmarks.java @@ -0,0 +1,112 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ. If not, see . + */ +/** + * Changes + */ +package duskz.client; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; + +/** + * Bookmark format is host:port + * + * TODO: store username too? + * + * @author notzed + */ +public class Bookmarks { + + List bookmarks = new ArrayList<>(); + + public List getBookmarks() { + return bookmarks; + } + + public void setBookmarks(List bookmarks) { + this.bookmarks = bookmarks; + } + + public Bookmarks() { + } + + public void load(String path) throws IOException { + try (BufferedReader br = Files.newBufferedReader(Paths.get(path), Charset.forName("UTF-8"))) { + String l; + + while ((l = br.readLine()) != null) { + try { + bookmarks.add(Bookmark.valueOf(l)); + } catch (NumberFormatException ex) { + } + } + } catch (NoSuchFileException x) { + // ignore it + } + } + + public void save(String path) throws IOException { + try (BufferedWriter br = Files.newBufferedWriter(Paths.get(path), Charset.forName("UTF-8"), StandardOpenOption.CREATE, + StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) { + for (Bookmark b : bookmarks) { + b.write(br); + } + } + } + + public static class Bookmark { + + public String host; + public int port; + + public Bookmark(String host, int port) { + this.host = host; + this.port = port; + } + + public static Bookmark valueOf(String v) { + int p = v.indexOf(':'); + if (p != -1) { + return new Bookmark(v.substring(0, p), Integer.valueOf(v.substring(p + 1))); + } else { + return new Bookmark(v, 7400); + } + } + + private void write(BufferedWriter br) throws IOException { + br.write(host); + br.write(':'); + br.write(Integer.toString(port)); + br.write('\n'); + } + + @Override + public String toString() { + return host + ":" + port; + } + } +} diff --git a/DuskZ/src/duskz/client/ClientMap.java b/DuskZ/src/duskz/client/ClientMap.java new file mode 100644 index 0000000..b39a324 --- /dev/null +++ b/DuskZ/src/duskz/client/ClientMap.java @@ -0,0 +1,280 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ. If not, see . + */ +/** + * Changes + */ +package duskz.client; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +/** + * Client map has a fixed set of tiles for the background. And then a bunch of + * entities roaming across it. + * + * I experimented here with a different implementation to the server map in that + * the entities by location uses a hash table rather than an array. This allows + * me to reference them by absolute coordinates which simplifies a lot of the + * code. + * + * @author notzed + */ +public class ClientMap { + + /** + * Offset of map (top-left corner) + */ + public int offx, offy; + /** + * Tile indices for images + */ + private short[] tiles; + /** + * Entities by id + */ + private HashMap entityByID = new HashMap<>(); + /** + * Entities by name + * TODO: If names do not need unique 'numbers' then this + * index can be completely removed. + */ + private HashMap> entityByName = new HashMap<>(); + /** + * Enities by location. Location is encoded as ( y << 16) | x + */ + private HashMap> entityByLocation = new HashMap<>(); + /** + * Width of map shown + */ + public final int cols; + /** + * Height of map shown + */ + public final int rows; + + public ClientMap(int cols, int rows) { + this.cols = cols; + this.rows = rows; + + tiles = new short[cols * rows]; + } + + public Collection getEntities() { + return entityByID.values(); + } + + public Collection getEntities(int x, int y) { + return entityByLocation.get(locationKey(x, y)); + } + + public int getTile(int x, int y) { + return tiles[x + y * cols]; + } + + public void setTile(int x, int y, int tile) { + tiles[x + y * cols] = (short) tile; + } + + private Integer locationKey(int x, int y) { + return (y << 16) | x; + } + + private Integer locationKey(Entity e) { + return (e.locy << 16) | e.locx; + } + + public int mapToLocalX(int x) { + return x - offx; + } + + public int mapToLocalY(int y) { + return y - offy; + } + + private void addLocation(Entity e) { + // Index by absolute location. + Integer loc = locationKey(e); + List bypos = entityByLocation.get(loc); + if (bypos == null) { + bypos = new ArrayList<>(); + entityByLocation.put(loc, bypos); + } + bypos.add(e); + } + + private void removeLocation(Entity e) { + Integer loc = locationKey(e); + List bypos = entityByLocation.get(loc); + + if (bypos == null || !bypos.remove(e)) + throw new RuntimeException("Entity not in location index"); + + if (bypos.isEmpty()) + entityByLocation.remove(loc); + } + + public synchronized void addEntity(Entity e) { + if (entityByID.containsKey(e.ID)) { + throw new RuntimeException("entity already in table " + e.ID + " " + e.strName); + } + + entityByID.put(e.ID, e); + + // Add byname index, and re-number if required + List byname = entityByName.get(e.getSimpleName()); + if (byname == null) { + byname = new ArrayList<>(); + entityByName.put(e.getSimpleName(), byname); + } + byname.add(e); + //for (int i = 0; i < byname.size(); i++) + // byname.get(i).intNum = i; + + addLocation(e); + } + + public synchronized void moveEntity(Entity e, int tox, int toy) { + removeLocation(e); + e.locx = tox; + e.locy = toy; + addLocation(e); + } + + public synchronized void removeEntity(Entity e) { + removeEntity(e.ID); + } + + public synchronized void removeEntity(long id) { + Entity e = entityByID.remove(id); + + if (e == null) { + System.out.println("removeEntity: no such entity " + id); + return; + } + + List byname = entityByName.get(e.getSimpleName()); + byname.remove(e); + if (byname.isEmpty()) { + entityByName.remove(e.getSimpleName()); + } else { + //for (int i = 0; i < byname.size(); i++) + // byname.get(i).intNum = i; + } + + removeLocation(e); + } + + public synchronized Entity getEntity(long id) { + return entityByID.get(id); + } + + boolean inside(int x, int y) { + int lx = x - offx; + int ly = y - offy; + + return lx >= 0 && lx < cols + && ly >= 0 && ly < rows; + } + + public synchronized void updateTiles(int offx, int offy, DataInputStream instream) throws IOException { + //for (int x = 0; x < cols; x++) { + // for (int y = 0; y < rows; y++) { + // setTile(x, y, Short.parseShort(instream.readLine())); + // } + //} + int length = instream.readInt(); + if (length != cols * rows) { + throw new IOException("Protocol error incorrect map size"); + } + for (int y = 0; y < rows; y++) { + for (int x = 0; x < cols; x++) { + setTile(x, y, instream.readShort()); + } + } + + this.offx = offx; + this.offy = offy; + + // Prune out of range thingies + List removing = new ArrayList<>(); + for (Entry> es : entityByLocation.entrySet()) { + int ex = es.getKey() & 0xffff; + int ey = (es.getKey() >> 16) & 0xffff; + + System.out.printf("check inside %d,%d = %s\n", ex, ey, inside(ex, ey)); + + if (!inside(ex, ey)) { + removing.addAll(es.getValue()); + } + } + + // TODO: this could be optimised + // TODO: does the server need to know this + if (!removing.isEmpty()) { + System.out.println("offset: " + this.offx + "," + this.offy); + for (Entity e : removing) { + System.out.println("removing out of range :" + e); + removeEntity(e); + } + } + } + + public synchronized void updateTiles(int offx, int offy, short[] tiles) throws IOException { + //for (int x = 0; x < cols; x++) { + // for (int y = 0; y < rows; y++) { + // setTile(x, y, Short.parseShort(instream.readLine())); + // } + //} + if (tiles.length != rows * cols) { + throw new IOException("Protocol error incorrect map size"); + } + System.arraycopy(tiles, 0, this.tiles, 0, tiles.length); + + this.offx = offx; + this.offy = offy; + + // Prune out of range thingies + List removing = new ArrayList<>(); + for (Entry> es : entityByLocation.entrySet()) { + int ex = es.getKey() & 0xffff; + int ey = (es.getKey() >> 16) & 0xffff; + + System.out.printf("check inside %d,%d = %s\n", ex, ey, inside(ex, ey)); + + if (!inside(ex, ey)) { + removing.addAll(es.getValue()); + } + } + + // TODO: this could be optimised + // TODO: does the server need to know this + if (!removing.isEmpty()) { + System.out.println("offset: " + this.offx + "," + this.offy); + for (Entity e : removing) { + System.out.println("removing out of range :" + e); + removeEntity(e); + } + } + } +} diff --git a/DuskZ/src/duskz/client/DataManager.java b/DuskZ/src/duskz/client/DataManager.java new file mode 100644 index 0000000..52a0249 --- /dev/null +++ b/DuskZ/src/duskz/client/DataManager.java @@ -0,0 +1,139 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ. If not, see . + */ +/** + * Changes + */ +package duskz.client; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; + +/** + * Utility for loading game objects from jar file + * and managing them + * + * TODO: i couldn't work out how to do generics with the inner class thing + * + * @author notzed + */ +public class DataManager { + + String source; + protected ImageSet[] tilesets; + + public DataManager(String source) { + this.source = source; + } + + public void open() throws IOException { + tilesets = getImageSet("tilesets"); + // TODO: sprites, etc. + + for (ImageSet is : tilesets) + is.load(); + } + + private int getInt(Attributes a, int index, String name) { + try { + return Integer.valueOf(a.getValue(index + "_" + name)); + } catch (NumberFormatException x) { + return -1; + } + } + + protected ImageSet createImageSet() { + return new ImageSet(); + } + + JarInputStream openJar() throws IOException { + return new JarInputStream(new FileInputStream(source)); + } + + public ImageSet[] getImageSet(String setName) throws IOException { + List list; + try (JarInputStream jis = openJar()) { + list = new ArrayList<>(); + Manifest man = jis.getManifest(); + Attributes a = man.getAttributes(setName); + int count = Integer.valueOf(a.getValue("count")); + for (int i = 0; i < count; i++) { + ImageSet set = createImageSet(); + + set.name = a.getValue(i + "_name"); + set.source = a.getValue(i + "_source"); + set.gid = getInt(a, i, "gid"); + set.count = getInt(a, i, "count"); + set.width = getInt(a, i, "width"); + set.height = getInt(a, i, "height"); + + // Load it straight away? + + list.add(set); + } + } + + return list.toArray(new ImageSet[list.size()]); + } + + public class ImageSet { + + public String name; + public String source; + public int gid; + public int count; + public int width; + public int height; + + public ImageSet() { + } + + public void load() throws IOException { + } + + public InputStream getInputStream() throws IOException { + JarInputStream jis = null; + try { + jis = openJar(); + JarEntry je; + + System.out.println("Looking for: " + source); + + while ((je = jis.getNextJarEntry()) != null) { + System.out.println("je = " + je.getName()); + if (je.getName().equals(source)) + return jis; + } + jis.close(); + throw new FileNotFoundException(source); + } catch (IOException ex) { + if (jis != null) + jis.close(); + throw ex; + } + } + } +} diff --git a/DuskZ/src/duskz/client/Direction.java b/DuskZ/src/duskz/client/Direction.java new file mode 100644 index 0000000..42ae5ac --- /dev/null +++ b/DuskZ/src/duskz/client/Direction.java @@ -0,0 +1,37 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ. If not, see . + */ +package duskz.client; + +/** + * Enumeration for direction commands. + * @author notzed + */ +public enum Direction { + North("n"), + South("s"), + East("e"), + West("w"); + + String cmd; + private Direction(String cmd) { + this.cmd = cmd; + } + + +} diff --git a/DuskZ/src/duskz/client/Dusk.java b/DuskZ/src/duskz/client/Dusk.java new file mode 100644 index 0000000..aedb57f --- /dev/null +++ b/DuskZ/src/duskz/client/Dusk.java @@ -0,0 +1,961 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Special Thanks to: + * Randall Leeds for the following code portions + * as well as many other small changes and deprecation fixes: + * Float/Unfloat + * Joe Alloway for shadowed text and the !set command + * + * Changes + * Feb-2013 Michael Zucchi - modernised java, cleanup, abstracted the GUI behind + * an interface. Abstracted most commands. Added some binary protocol changes. + * Track the player id for better battles. + */ +package duskz.client; + +import duskz.protocol.MessageType; +import duskz.protocol.ServerMessage; +import duskz.protocol.ServerMessage.EntityMessage; +import java.io.*; +import java.net.*; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * TODO: I've removed text colour text for now, implementation + * was poor TODO: applet stuff is removed FIXME: sound is all disabled + * + * @author notzed + */ +public class Dusk implements Runnable { + + public final static boolean dumpRaw = false; + static String strVersion = "2.7.1.W3"; + Status status = new Status(); + long loncash; + boolean loaded, + blnMenuRefresh, + connected = false, + blnApplet; + int intMusicTypes; + int intNumSongs[]; + // FIXME: applet will have a different frontend or something + //AudioClip audSFX[], audMusic[][]; + //AudioClip audMusicPlaying; + String rcLocation; + List buyList = new ArrayList<>(); + List sellList = new ArrayList<>(); + Equipment worn = new Equipment(); + Socket socket; + DataOutputStream outstream; + DataInputStream instream; + int mapSize = 11; + ClientMap map; + boolean intStep, + blnMusic = true; + Thread thrRun; + int tileSize = 32; + int spriteSize = 64; + GUI frame; + String address = "dusk.wesowin.org"; + int port = 7423; + + public Dusk(GUI gui) { + frame = gui; + frame.setGame(this); + } + + public void startUp() { + try { + //frame.initComponents(); + //frame.setVisible(true); + frame.logClear(); + frame.log("Dusk Client v" + strVersion + " -- http://FIXME/\n"); + frame.log("You are using Java version " + System.getProperty("java.version") + "\n"); + + if (blnApplet) { + //address = appShell.getParameter("address"); + //port = Integer.parseInt(appShell.getParameter("port")); + connect(); + } else { + frame.showLogin(); + } + //thrGraphics = new GraphicsThread(this); + // TODO: check if i need to set map images here + } catch (Exception e) { + e.printStackTrace(); + } + } + + // Main entry point + //static public void main(String[] args) { + // new Dusk(); + //} + void connect() { + throw new UnsupportedOperationException(); + } + + // initial cut at automatic login stuff + // probably want to fix server to handle login/new account stuff separately + enum State { + // not connected + + Unconnected, + // tcp connection made + Connected, + // Username sent + Username, + // Create a new user + Create, + // Password sent + Password, + // Password accepted + Ready, + // Waiting for race selection + SelectRace, + } + State state = State.Unconnected; + String pass; + + public void connect(String address, int port, String user, String pass) { + // Connect to Server + try { + socket = new Socket(address, port); + outstream = new DataOutputStream(socket.getOutputStream()); + instream = new DataInputStream(new BufferedInputStream(socket.getInputStream())); + log("Please enter your character name or the name of a new character: \n"); + + state = State.Connected; + + // Load Images and start checking for incoming commands + thrRun = new Thread(this); + thrRun.start(); + connected = true; + //Initialize objects + map = new ClientMap(11, 11); + buyList.clear(); + sellList.clear(); + + this.pass = pass; + state = State.Username; + command(user); + } catch (Exception e) { + log("Error connecting to server: " + e.toString() + "\n"); + return; + } + } + + /** + * FIXME: all needs a rewrite + * + * @param entStore + */ + // add the thing, this renumbers any that have the same name + void addEntity(Entity entStore) { + System.out.println("Add entity: " + entStore.ID + " " + entStore.strName); + map.addEntity(entStore); + + // FIXME: call rement if it was out of range so the server knows + } + + Entity findEntity(long id) { + return map.getEntity(id); + } + + void removeEntity(long id) { + map.removeEntity(id); + } + + void removeEntity(Entity entStore) { + map.removeEntity(entStore); + } + + /** + * Read a . terminated list from server + * + * @return + */ + List readList() throws IOException { + List l = new ArrayList<>(); + + String s = instream.readLine(); + while (!s.equals(".")) { + l.add(s); + s = instream.readLine(); + } + return l; + } + + String readContent(String end) throws IOException { + StringBuilder sb = new StringBuilder(); + String s = instream.readLine(); + + while (!s.equals(end)) { + sb.append(s).append('\n'); + s = instream.readLine(); + } + return sb.toString(); + } + + //Thread to process incoming commands + public void run() { + int incoming = 0; + while (incoming != -1) { + try { + // Handle incoming messages from Server +// incoming = stmIn.readByte(); + incoming = instream.read(); + + if (incoming == -1) { + continue; + } + + MessageType type = MessageType.fromServer(incoming); + + System.out.println("state " + state + " incoming = " + type); + + // Hack to auto-login to server + switch (state) { + case Username: + if (type == MessageType.Chat) { + String s = instream.readLine(); + System.out.println("username: text: " + s); + if (s.equals("enter your password:")) { + state = State.Password; + command(pass); + } else if (s.endsWith("Is that correct? (yes/no)")) { + // "new user" shit + state = state.Create; + command("yes"); + } + } + continue; + case Password: + if (type == MessageType.Chat) { + String s = instream.readLine(); + System.out.println("password: text: " + s); + if (s.equals("Login Accepted.")) { + state = State.Ready; + frame.loginOk(); + } else if (s.equals("Incorrect Password.")) { + // blah blah what now? + state = state.Connected; + } + } + continue; + case Create: + if (type == MessageType.Chat) { + String s = instream.readLine(); + System.out.println("create: text: " + s); + if (s.equals("Enter a new password:")) { + command(pass); + } else if (s.equals("Confirm that password:")) { + //state = State.Password; + command(pass); + } else if (s.equals("Login Accepted.")) { + // this should now ask for a race + state = State.Ready; + frame.loginOk(); + } + // TODO: find out what happens if that doesn't work + } + continue; + case SelectRace: + switch (type) { + case ChooseRace: + // Bad choice, try again + handleChooseRace(instream); + continue; + default: + state = State.Ready; + // any other message drop through to process + } + break; + } + + switch (type) { + case Quit: //Quit + { + loaded = false; + connected = false; + socket.close(); + return; + } + case UpdateImages: //update Images + { + rcLocation = instream.readLine(); + // FIXME: not if applet or something? + rcLocation = "rc/" + rcLocation; + + frame.setImages(new File(rcLocation + "/images/map.gif").toURI().toString(), 32, + new File(rcLocation + "/images/players.gif").toURI().toString(), 64, + new File(rcLocation + "/images/sprites.gif").toURI().toString(), 64); + update(); + break; + } + case UpdateLocMap: { + /* + status.updateLocation(instream); + System.out.printf("Updating map to %d %d map size %d %d\n", + status.locx, status.locy, mapSize, mapSize); + map.updateTiles(status.locx - (map.cols - 1) / 2, status.locy - (map.rows - 1) / 2, instream); + */ + ServerMessage.MapMessage msg = ServerMessage.MapMessage.decode(instream); + status.updateLocation(msg.x, msg.y); + map.updateTiles(msg.x - (map.cols - 1) / 2, msg.y - (map.rows - 1) / 2, msg.map); + update(); + frame.setStatus(status); + //frame.info.setText("HP: " + inthp + "/" + intmaxhp + " MP: " + intsp + "/" + intmaxsp + " Loc: " + LocX + "/" + LocY); + buyList.clear(); + reloadChoiceLookGetAttack(); + //paint(); + break; + } + case Chat: //incoming chat + { + chat(instream.readLine() + "\n"); + break; + } + case AddEntity: //add Entity + { + /* + String s = instream.readLine(); + int etype = Byte.parseByte(instream.readLine()); + Entity ent = null; + if (etype == 0) { + try { + ent = new Entity(s, + Long.parseLong(instream.readLine()), + Integer.parseInt(instream.readLine()), + Integer.parseInt(instream.readLine()), + Integer.parseInt(instream.readLine()), + Integer.parseInt(instream.readLine()), + etype); + } catch (NullPointerException e) { + ent = null; + } + } else { + try { + ent = new Entity(s, + Long.parseLong(instream.readLine()), + Integer.parseInt(instream.readLine()), + Integer.parseInt(instream.readLine()), + Integer.parseInt(instream.readLine()), + -1, + etype); + } catch (NullPointerException e) { + ent = null; + } + }*/ + EntityMessage msg = ServerMessage.EntityMessage.decode(type, instream); + if (msg != null) { + addEntity(new Entity(msg)); + } + reloadChoiceLookGetAttack(); + update(); + break; + } + case UpdateStats: //update Stats + { + status.updateStatus(instream); + frame.setStatus(status); + break; + } + case UpdateItems: //update Items + { + worn.updateAvailable(instream); + frame.setDropList(worn.all); + frame.setEquipment(worn); + break; + } + case UpdateEquipment: //update Equipment + { + worn.updateWorn(instream); + frame.setEquipment(worn); + break; + } + case UpdateInfo: //update Stats + { + try { + StringBuilder sb = new StringBuilder(); + String s = instream.readLine(); + while (!s.equals(".")) { + sb.append(s); + sb.append("\n"); + s = instream.readLine(); + } + frame.setStats(sb.toString()); + } catch (Exception e) { + System.err.println("Error loading stats" + e.toString()); + } + break; + } + case Halt: //halt + { + loaded = false; + break; + } + case UpdateActions: //update Actions + { + frame.setActionList(readList()); + break; + } + case LoadMusic: //load music + { + try { + // FIXME; applet + /* + if (blnApplet) { + log("Loading music.\n"); + intMusicTypes = Integer.parseInt(instream.readLine()); + audMusic = new AudioClip[intMusicTypes][]; + intNumSongs = new int[intMusicTypes]; + for (intStore = 0; intStore < intMusicTypes; intStore++) { + intNumSongs[intStore] = Integer.parseInt(instream.readLine()); + audMusic[intStore] = new AudioClip[intNumSongs[intStore]]; + for (intStore2 = 0; intStore2 < intNumSongs[intStore]; intStore2++) { + String s = instream.readLine(); + try { + //audMusic[intStore][intStore2] = appShell.getAudioClip(new URL(strStore)); + while (audMusic[intStore][intStore2] == null) { + } + } catch (Exception e) { + System.err.println("Error while trying to load music file " + s + ":" + e.toString()); + } + } + } + log("Music loaded.\n"); + }*/ + } catch (Exception e) { + blnMusic = false; + log("Your java virtual machine does not support midi music\n"); + System.err.println("Error while trying to load music files:" + e.toString()); + } + break; + } + case PlayMusic: //play music + { + if (blnMusic) { + /* FIXME: sound + try { + intStore = Integer.parseInt(instream.readLine()); + if (audMusicPlaying != null) { + audMusicPlaying.stop(); + } + audMusicPlaying = audMusic[intStore][(int) (Math.random() * intNumSongs[intStore])]; + audMusicPlaying.loop(); + } catch (Exception e) { + System.err.println("Error while trying to play music file:" + e.toString()); + }*/ + } + break; + } + case Ping: //stillThere? + { + outstream.writeBytes("notdead\n"); + break; + } + case Proceed: //proceed + { + status.id = Long.parseLong(instream.readLine()); + loaded = true; + break; + } + case PlaySound: //play sfx + { + try { + //audSFX[Integer.parseInt(instream.readLine())].play(); + } catch (Exception e) { + } + break; + } + case RemoveEntity: //remove entity + { + long id = Long.valueOf(instream.readLine()).longValue(); + + removeEntity(id); + reloadChoiceLookGetAttack(); + update(); + break; + } + case UpdateMerchant: //update Merchant + { + buyList = TransactionItem.createItems(readList()); + frame.setBuyList(buyList); + break; + } + case EditText: //view/edit + { + String name = instream.readLine(); + frame.visitFile(name, readContent("--EOF--"), true); + break; + } + case ResizeMap: //resize Map + { + mapSize = Integer.parseInt(instream.readLine()); + map = new ClientMap(mapSize, mapSize); + if (blnApplet) { + outstream.writeBytes("appletimages\n"); + } else { + outstream.writeBytes("applicationimages\n"); + } + //scaleWindow(); + break; + } + case ViewText: //view/no-edit + { + String name = instream.readLine(); + frame.visitFile(name, readContent("--EOF--"), false); + break; + } + case ExitMerchant: //offMerchant + { + frame.exitShop(); + buyList.clear(); + sellList.clear(); + break; + } + case UpdateSell: //update Sell + { + sellList = TransactionItem.createItems(readList()); + frame.setSellList(sellList); + break; + } + case ColourChat: //colour chat + { + // FIXME: re-implement colour, or probably fix it (hex codes) + instream.readLine(); + instream.readLine(); + instream.readLine(); + chat(instream.readLine() + "\n"); + break; + } + case MoveNorth: //move north + { + long ID = Long.parseLong(instream.readLine()); + + handleMove(ID, 1, 0, -1, 0); + break; + } + case MoveSouth: //move south + { + long ID = Long.parseLong(instream.readLine()); + + handleMove(ID, 3, 0, 1, 1); + break; + } + case MoveWest: //move west + { + long ID = Long.parseLong(instream.readLine()); + + handleMove(ID, 5, -1, 0, 2); + break; + } + case MoveEast: //move east + { + long ID = Long.parseLong(instream.readLine()); + + handleMove(ID, 7, 1, 0, 3); + break; + } + case UpdateRange: //update range + { + status.range = Integer.parseInt(instream.readLine()); + break; + } + case SetFlag: //set flag + { + long id = Long.valueOf(instream.readLine()).longValue(); + int flags = Integer.parseInt(instream.readLine()); + + Entity e = map.getEntity(id); + if (e != null) { + e.intFlag = flags; + update(); + } else { + log("Error: set flag on unknown entity: " + id); + } + break; + } + case ClearFlags: //clear all flags + { + // FIXME: hideinfo? + synchronized (map) { + for (Entity e : map.getEntities()) { + e.intFlag = 0; + } + } + update(); + break; + } + case StartBattle: //show battle window and clear text + { + // FIXME: no battle popup window at the moment + String s = instream.readLine(); + battle("Battle started: " + s + "\n"); + break; + } + case UpdateBattle: //show battle window and update title + { + // FIXME: no battle popup window at the moment + String s = instream.readLine(); + battle("Battle status: " + s + "\n"); + break; + } + case LogBattle: //add text to battle window + { + // FIXME: no battle popup window at the moment + String s = instream.readLine(); + battle(s + "\n"); + break; + } + case ChooseRace: + handleChooseRace(instream); + break; + case HitEntity: { + long toID = Long.valueOf(instream.readLine()); + int delta = -Integer.valueOf(instream.readLine()); + int newhp = Integer.valueOf(instream.readLine()); + int tothp = Integer.valueOf(instream.readLine()); + long fromID = Long.valueOf(instream.readLine()); + String how = instream.readLine(); + + String s; + do { + s = instream.readLine(); + } while (!s.equals(".")); + + System.out.printf("entity %d %s delta %d hp %d/%d\n", + toID, how, delta, newhp, tothp); + + // TODO: I should have who from/to this damage is + Entity f = findEntity(fromID); + Entity e = findEntity(toID); + if (e != null) { + double xloc = map.mapToLocalX(e.locx), + yloc = map.mapToLocalY(e.locy); + + // Get angle to entity from player + double a = Math.PI; + if (f != null) { + a = Math.atan2(e.locy - f.locy, e.locx - f.locx); + } + + frame.damageEntity(e, a, xloc * tileSize, yloc * tileSize, delta, how); + } else { + System.out.println("Unknown entity"); + } + + break; + } + } + } catch (Exception e) { + System.err.println("Error at run() with value " + incoming + " : " + e.toString()); + e.printStackTrace(System.out); + + log("Error at run() with value " + incoming + " : " + e.toString() + "\n"); + connected = false; + return; + } + } + System.err.println("Error at run() with value " + incoming); + } + + private void handleMove(long ID, int step, int dx, int dy, int dir) { + Entity e = map.getEntity(ID); + + if (e == null) { + System.out.println("move unknown entity: " + ID); + return; + } + + map.moveEntity(e, e.locx + dx, e.locy + dy); + if (e.intStep != -1) { + // Toggle through the 2 animations for movement + if (e.intMoveDirection == dir) { + e.intStep ^= 1; + } else { + e.intStep = step; + e.intMoveDirection = dir; + } + } + reloadChoiceAttack(); + // TODO: find out what this is for + //if (addit) { + // thrGraphics.addEntityToMove(ent, dir); + //} + // HACK: Don't update the screen display if the user moves, attempted + // fix at flicker-on-move stuff. + // The server should always send an UpdateLocMap every time + if (ID != status.id) { + update(); + } + } + + private void handleChooseRace(DataInputStream in) throws IOException { + List races = new ArrayList<>(); + String prompt = in.readLine(); + String l = in.readLine(); + while (!l.equals(".")) { + races.add(l); + l = in.readLine(); + } + state = State.SelectRace; + frame.chooseRace(prompt, races); + } + + public void chooseRace(String race) { + if (state == State.SelectRace) { + command(race); + } + } + + // TBD - use the individual functions + public void reloadChoiceLookGetAttack() { + final ArrayList looks = new ArrayList<>(); + final ArrayList gets = new ArrayList<>(); + final ArrayList attacks = new ArrayList<>(); + + synchronized (map) { + for (Entity e : map.getEntities()) { + if (status.canLook(e)) + looks.add(e); + + if (status.canTake(e)) + gets.add(e); + + if (status.canAttack(e)) + attacks.add(e); + } + } + + frame.setLookList(looks); + frame.setAttackList(attacks); + frame.setTakeList(gets); + } + + public void reloadChoiceLook() { + final ArrayList looks = new ArrayList<>(); + + synchronized (map) { + for (Entity e : map.getEntities()) { + if (status.canLook(e)) + looks.add(e); + } + } + frame.setLookList(looks); + } + + public void reloadChoiceAttack() { + final ArrayList attacks = new ArrayList<>(); + + synchronized (map) { + for (Entity e : map.getEntities()) { + if (status.canAttack(e)) + attacks.add(e); + } + } + frame.setAttackList(attacks); + } + + public void reloadChoiceGet() { + final ArrayList gets = new ArrayList<>(); + + synchronized (map) { + for (Entity e : map.getEntities()) { + if (status.canTake(e)) + gets.add(e); + } + } + frame.setTakeList(gets); + } + + public boolean isConnected() { + return connected; + } + + public boolean isLoaded() { + return loaded; + } + + public int getTileSize() { + return tileSize; + } + + /** + * Offset y to origin of displayed map + * + * @return + */ + public int getMapOffsetX() { + return map.offx; + } + + /** + * Offset y to origin of displayed map + * + * @return + */ + public int getMapOffsetY() { + return map.offy; + } + + public void userconnect() { + //frame.frmConnect.show(); + //frame.frmConnect.setSize(350, 200); + System.out.println("user ocnnect not implemented - quit and restart"); + } + + public void equipment() { + frame.manageEquipment(worn); + } + + public void merchant() { + frame.enterShop("Merchant", buyList, sellList); + } + + // these all do the same thing at the moment, but could allow + // for alternative output for various things + // FIXME: chat needs the source id as well + public void chat(String txt) { + frame.log(txt); + } + + public void battle(String txt) { + System.out.println("battle: " + txt); + frame.log(txt); + } + + public void log(String txt) { + frame.log(txt); + } + + public void logout() { + try { + if (connected) { + outstream.writeBytes("quit\n"); + } + } catch (Exception exc) { + } + try { + socket.close(); + } catch (Exception exc) { + } + socket = null; + connected = false; + } + + public void quit() { + try { + if (connected) { + outstream.writeBytes("quit\n"); + } + } catch (Exception exc) { + } + try { + socket.close(); + } catch (Exception exc) { + } + System.gc(); + if (blnApplet) { + //appShell.destroy(); + } else { + // FIXME: wrong! + System.exit(0); + } + + } + + //Display graphics + public void update() { + synchronized (map) { + frame.updateMap(map); + } + } + + private void docmd(String cmd) { + System.out.println("send command: " + cmd); + try { + outstream.writeBytes(cmd); + } catch (IOException ex) { + // TODO: close connection here + Logger.getLogger(Dusk.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public void command(String what) { + docmd(what + "\n"); + } + + public void command(String what, String params) { + docmd(what + " " + params + "\n"); + } + + public void move(Direction dir) { + command(dir.cmd); + } + + public void moveTo(int dx, int dy) { + //Move to location + try { + System.out.println("goto " + dx + "," + dy); + outstream.writeBytes("goto " + dx + " " + dy + "\n"); + } catch (Exception e) { + frame.log("Error in goto %d,%d: %s", dx, dy, e); + } + } + + public void buy(TransactionItem item, int quantity) { + command("buy " + quantity + " " + item.name); + } + + public void sell(TransactionItem item, int quantity) { + command("sell " + quantity + " " + item.name); + } + + // TODO: find out why these uses the base name and ignores the numbers. + public void attack(Entity e) { + command("a " + e.ID); + } + + public void look(Entity e) { + command("look " + e.ID); + } + + public void take(Entity e) { + command("get " + e.ID); + } + + public void drop(String what) { + command("drop " + what); + } + + public void wear(String what) { + command("wear " + what); + } + + public void unwear(String what) { + command("unwear " + what); + } +} diff --git a/DuskZ/src/duskz/client/Entity.java b/DuskZ/src/duskz/client/Entity.java new file mode 100644 index 0000000..f141c10 --- /dev/null +++ b/DuskZ/src/duskz/client/Entity.java @@ -0,0 +1,98 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - some helper/accessors. + */ +package duskz.client; + +import duskz.protocol.ServerMessage; + +public class Entity { + + public String strName; + public int locx, + locy; + public int intType, + intNum = 0, + intStep = -1, + intImage = 0, + intTicks, + intMoveDirection = -1, + /*intMoveDirection + 0 north + 1 south + 2 west + 3 east + */ + intFlag = 0; + /*intFlag + 0 none + 1 ally + 2 enemy + */ + long ID; + Entity entNext = null; + + public Entity(String instrName, long inID, int inintLocX, int inintLocY, int inImage, int inStep, int inintType) { + strName = instrName; + ID = inID; + locx = inintLocX; + locy = inintLocY; + intImage = inImage; + intStep = inStep; + intType = inintType; + } + + public Entity(ServerMessage.EntityMessage msg) { + strName = msg.name; + locx = msg.x; + locy = msg.y; + ID = msg.id; + intType = msg.entityType; + intImage = msg.image; + intStep = msg.imageStep; + } + + /** + * Returns name without status attributes + * + * @return + */ + public String getSimpleName() { + int i = strName.lastIndexOf(">"); + if (i != -1) { + return strName.substring(i + 1); + } else { + return strName; + } + } + + public String getIndexedName() { + return intNum == 0 ? strName : intNum + "." + strName; + } + + @Override + public String toString() { + + return "[Entity " + ID + ", " + strName + ", " + locx + ", " + locy + "]"; + } +} diff --git a/DuskZ/src/duskz/client/Equipment.java b/DuskZ/src/duskz/client/Equipment.java new file mode 100644 index 0000000..b988c77 --- /dev/null +++ b/DuskZ/src/duskz/client/Equipment.java @@ -0,0 +1,117 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ. If not, see . + */ +/** + * Changes + */ +package duskz.client; + +import duskz.protocol.Wearing; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * This is a bit junk. + * Server code also has a similar mess + * + * @author notzed + */ +public class Equipment implements Wearing { + + /** + * Worn items + */ + public String[] worn = new String[WEARING_COUNT]; + /** + * Available items for wearing + */ + public List[] available = new List[WEARING_COUNT]; + /** + * All items + */ + public List all = new ArrayList<>(); + /** + * Where is item wearable at? + * FIXME: maybe i should just store them in a structure + */ + HashMap wearableAt = new HashMap<>(); + + public Equipment() { + for (int i = 0; i < available.length; i++) + available[i] = new ArrayList<>(); + } + + public int canWearAt(String what) { + Integer i = wearableAt.get(what); + + if (i == null) + return -1; + return i; + } + + /** + * Returns where what is worn at, if at all. + * + * @param what + * @return -1 if not worn + */ + public int wearingAt(String what) { + for (int i = 0; i < worn.length; i++) + if (worn[i].equals(what)) + return i; + return -1; + } + + /** + * Decode worn from network + * + * @return + */ + public void updateWorn(DataInputStream instream) throws IOException { + for (int i = 0; i < worn.length; i++) { + worn[i] = instream.readLine(); + System.out.println(" worn: " + worn[i]); + } + } + + public void updateAvailable(DataInputStream instream) throws IOException, NumberFormatException { + String line; + + all.clear(); + for (int i = 0; i < available.length; i++) + available[i].clear(); + wearableAt.clear(); + + while ((line = instream.readLine()) != null && !line.equals(".")) { + // Need to map from server 'id' to our index + int i = Integer.parseInt(line) - 1; + String s = instream.readLine(); + + System.out.println(" " + i + " " + s); + + if (i >= 0 && i < available.length) { + available[i].add(s); + wearableAt.put(s, i); + } + all.add(s); + } + } +} diff --git a/DuskZ/src/duskz/client/GUI.java b/DuskZ/src/duskz/client/GUI.java new file mode 100644 index 0000000..a6dace1 --- /dev/null +++ b/DuskZ/src/duskz/client/GUI.java @@ -0,0 +1,146 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ. If not, see . + */ +/** + * Changes + */ +package duskz.client; + +import java.util.List; + +/** + * Interface for all gui interactions. + * + * To implement a different frontend toolkit, simply pass this to the Dusk + * constructor. + * + * @author notzed + */ +public interface GUI { + + /** + * Init, should be called before anything else + */ + public void setGame(Dusk aThis); + + /** + * Clear output log, i.e. start of game + */ + public void logClear(); + + /** + * Log something to the log window + * + * @param txt + */ + public GUI log(String txt, Object... args); + // Init/login + + public void showLogin(); + + public void loginOk(); + + public void chooseRace(String prompt, List races); + + /** + * Show the shop window + * + * @param title + * @param buyList + * @param sellList + */ + public void enterShop(String title, List buyList, List sellList); + + /** + * Update the buy list whilst in the shop + * + * @param buyList + */ + public void setBuyList(List buyList); + + /** + * Update the sell list whilst in the shop + * + * @param sellList + */ + public void setSellList(List sellList); + + /** + * Close the shop window + */ + public void exitShop(); + + /** + * Open the equipment window + * + * @param worn + */ + public void manageEquipment(Equipment worn); + + /** + * Update the won list, i.e. if in inventory/equipment screen + * + * @param worn + */ + public void setEquipment(Equipment worn); + + // Misc high level functions + public void visitFile(String file, String text, boolean canSave); + + // TODO: parse the content + public void setStats(String stats); + + /** + * Set hp/mp etc status. + * + * @param status + */ + public void setStatus(Status status); + + public void setTakeList(List list); + + public void setDropList(List list); + + public void setActionList(List list); + + public void setAttackList(List list); + + public void setLookList(List list); + + // setup/map + public void setImages(String tiles, int tileSize, String players, int playerSize, String sprites, int spriteSize); + + /** + * Update the map. + * + * @param map Source map + */ + public void updateMap(ClientMap map); + + /** + * Display or log some damage + * TODO: use a servermessage for the details + * @param e + * @param angle angle to attacker + * @param locx + * @param locy + * @param delta how much damage + * @param how how it was caused + */ + public void damageEntity(Entity e, double angle, double locx, double locy, int delta, String how); +} diff --git a/DuskZ/src/duskz/client/Status.java b/DuskZ/src/duskz/client/Status.java new file mode 100644 index 0000000..a6efd0e --- /dev/null +++ b/DuskZ/src/duskz/client/Status.java @@ -0,0 +1,87 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - encapsulated a bunch of code out of Dusk + * to track the status. + */ +package duskz.client; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * Current player information + * + * @author notzed + */ +public class Status { + + long id; + // Attack range + int range = 1; + int hp, maxhp; + int mp, maxmp; + int locx, locy; + + public void updateStatus(DataInputStream instream) throws IOException { + hp = Integer.parseInt(instream.readLine()); + maxhp = Integer.parseInt(instream.readLine()); + mp = Integer.parseInt(instream.readLine()); + maxmp = Integer.parseInt(instream.readLine()); + } + + public void updateLocation(int x, int y) { + locx = x; + locy = y; + } + + public void updateLocation(DataInputStream instream) throws IOException { + //locx = Integer.parseInt(instream.readLine()); + //locy = Integer.parseInt(instream.readLine()); + locx = instream.readShort(); + locy = instream.readShort(); + } + + @Override + public String toString() { + return "HP: " + hp + "/" + maxhp + " MP: " + mp + "/" + maxmp + " Loc: " + locx + "/" + locy; + } + + public int distance(Entity e) { + return Math.abs(locx - e.locx) + Math.abs(locy - e.locy); + } + + public boolean canLook(Entity e) { + return true; + } + + public boolean canTake(Entity e) { + return e.intType == 1 + && (distance(e) < 2); + } + + public boolean canAttack(Entity e) { + return e.ID != id + && ((e.intType == 0 || e.intType == 1 || e.intType == 4) + && (distance(e) <= range)); + } +} diff --git a/DuskZ/src/duskz/client/TransactionItem.java b/DuskZ/src/duskz/client/TransactionItem.java new file mode 100644 index 0000000..910b873 --- /dev/null +++ b/DuskZ/src/duskz/client/TransactionItem.java @@ -0,0 +1,99 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ. If not, see . + */ +package duskz.client; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Takes a server response and parses it into a name/cost/count + * list suitable for a toolkit. + * + * @deprecated should be merged with DuskCommon's TransactionItem + * @author notzed + */ +@Deprecated +public class TransactionItem implements Comparable { + + public String cost; + public int count; + public String name; + + /** + * Convert a list of items as from the server into a list of + * transaction items. + * i.e. count them up, separate cost. + * + * @param source + * @return + */ + public static List createItems(List source) { + List list = new ArrayList<>(); + + Collections.sort(source); + TransactionItem last = null; + String lastName = null; + for (String s : source) { + if (lastName != null && lastName.equals(s)) { + last.count++; + } else { + int i; + + last = new TransactionItem(); + last.count = 1; + + i = s.indexOf(')'); + if (i != -1) { + last.cost = s.substring(0, i); + last.name = s.substring(i + 1); + } else { + last.name = s; + last.cost = ""; + } + list.add(last); + } + lastName = s; + } + Collections.sort(list); + return list; + } + + public String getName() { + return name; + } + + public int getCount() { + return count; + } + + public String getCost() { + return cost; + } + + @Override + public String toString() { + return cost + ") " + name + "[" + count + "]"; + } + + @Override + public int compareTo(TransactionItem t) { + return name.compareTo(t.name); + } +} diff --git a/DuskZ/src/duskz/client/fx/DataManagerFX.java b/DuskZ/src/duskz/client/fx/DataManagerFX.java new file mode 100644 index 0000000..5720ed2 --- /dev/null +++ b/DuskZ/src/duskz/client/fx/DataManagerFX.java @@ -0,0 +1,100 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ. If not, see . + */ +/** + * Changes + */ +package duskz.client.fx; + +import duskz.client.DataManager; +import java.io.IOException; +import java.io.InputStream; +import javafx.geometry.Rectangle2D; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; + +/** + * Data manager for JavaFX - loads images into Image nodes and helpers + * to create positioned tiles. + * + * @author notzed + */ +public class DataManagerFX extends DataManager { + + public DataManagerFX(String source) { + super(source); + } + + @Override + protected ImageSet createImageSet() { + return new ImageSetFX(); + } + + /** + * Create a tile image and align it to the baseline of the map tile. + * + * This assumes the tiles are the same width but height may vary + * + * @param tileid + * @param tilex + * @param tiley + * @param tilewidth + * @param tileheight + * @return + */ + public ImageView createTile(int tileid, int tilex, int tiley, int tilewidth, int tileheight) { + try { + for (int i = 0; i < tilesets.length; i++) { + ImageSetFX is = (ImageSetFX) tilesets[i]; + if (tileid >= is.gid && tileid < is.gid + is.count) { + ImageView iv = new ImageView(is.image); + + tileid -= is.gid; + iv.setViewport(new Rectangle2D(tileid * is.width, 0, is.width, is.height)); + iv.relocate(tilex * tilewidth, tiley * tileheight - (is.height - tileheight)); + + return iv; + } + } + } catch (Exception x) { + x.printStackTrace(); + } + System.out.println("No such tile: " + tileid); + // Broken image? + return new ImageView(); + } + + public class ImageSetFX extends ImageSet { + + Image image; + + public ImageSetFX() { + } + + public void load() throws IOException { + System.out.println("Load tileset " + name + " gid " + gid + " count " + count); + try (InputStream s = getInputStream()) { + image = new Image(s); + } + } + + public Image getImage() { + return image; + } + } +} diff --git a/DuskZ/src/duskz/client/fx/DuskFX.java b/DuskZ/src/duskz/client/fx/DuskFX.java new file mode 100644 index 0000000..e7f8633 --- /dev/null +++ b/DuskZ/src/duskz/client/fx/DuskFX.java @@ -0,0 +1,78 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ. If not, see . + */ +/** + * Changes + */ +package duskz.client.fx; + +import duskz.client.Dusk; +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.stage.Stage; + +/** + * Main application entry point for JavaFX frontend + * + * @author notzed + */ +public class DuskFX extends Application { + + MainFrameFX frame; + Scene scene; + Dusk game; + + @Override + public void start(Stage stage) { + frame = new MainFrameFX(); + game = new Dusk(frame); + + scene = new Scene(frame); + stage.setScene(scene); + + scene.getAccelerators().put(new KeyCodeCombination(KeyCode.ESCAPE), new Runnable() { + @Override + public void run() { + frame.exitWindow(); + } + }); + + stage.getScene().getStylesheets().add(DuskFX.class.getResource("style.css").toExternalForm()); + stage.setTitle("DuskZ JavaFX"); + stage.show(); + + game.startUp(); + } + + @Override + public void stop() throws Exception { + game.quit(); + super.stop(); + } + + /** + * The main() method is ignored in correctly deployed JavaFX application. + * + * @param args the command line arguments + */ + public static void main(String[] args) { + launch(args); + } +} diff --git a/DuskZ/src/duskz/client/fx/EquipmentPane.java b/DuskZ/src/duskz/client/fx/EquipmentPane.java new file mode 100644 index 0000000..f515d48 --- /dev/null +++ b/DuskZ/src/duskz/client/fx/EquipmentPane.java @@ -0,0 +1,150 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ. If not, see . + */ +/** + * Changes + */ +package duskz.client.fx; + +import duskz.client.Equipment; +import java.util.ArrayList; +import java.util.List; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ListView; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import jfxtras.labs.scene.control.ListSpinner; + +/** + * Pane for a list of equipment with detail area and controls. + * + * @author notzed + */ +public class EquipmentPane extends HBox { + + ListView list; + Label detail; + ListSpinner quantity; + Button wear, unwear, drop; + Equipment worn; + + public EquipmentPane() { + setId("equipment-pane"); + + VBox right = new VBox(); + + HBox.setHgrow(right, Priority.ALWAYS); + + right.setAlignment(Pos.TOP_RIGHT); + right.setFillWidth(true); + + list = new ListView<>(); + detail = new Label(); + quantity = new ListSpinner(1, 100); + + HBox hb; + + hb = new HBox(); + hb.setFillHeight(true); + VBox.setVgrow(hb, Priority.ALWAYS); + hb.getChildren().add(detail); + right.getChildren().add(hb); + + wear = new Button("Wear"); + unwear = new Button("Un-wear"); + drop = new Button("Drop"); + + wear.setDisable(true); + unwear.setDisable(true); + drop.setDisable(true); + + hb = new HBox(); + hb.getChildren().addAll(wear, unwear, drop); + hb.setAlignment(Pos.CENTER); + right.getChildren().add(hb); + + HBox.setHgrow(list, Priority.NEVER); + + getChildren().addAll(list, right); + + list.setMinWidth(310); + list.setMaxWidth(310); + list.setEditable(false); + + list.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue ov, ItemInfo t, ItemInfo t1) { + if (t1 != null) { + detail.setText(String.format("Item: %s\n\n ... some description or shit ...\n", + t1)); + + wear.setDisable(t1.wornAt != -1 || worn.canWearAt(t1.name) == -1); + unwear.setDisable(t1.wornAt == -1); + drop.setDisable(false); + } else { + detail.setText(""); + wear.setDisable(true); + unwear.setDisable(true); + drop.setDisable(true); + } + } + }); + } + + public ItemInfo getItem() { + return list.getSelectionModel().getSelectedItem(); + } + + static class ItemInfo { + + String name; + int wornAt; + + public ItemInfo(String name, int wornAt) { + this.name = name; + this.wornAt = wornAt; + } + + @Override + public String toString() { + return wornAt == -1 ? name : name + " [worn: " + Equipment.titles[wornAt] + "]"; + } + } + + public void setEquipment(Equipment worn) { + this.worn = worn; + + List items = new ArrayList<>(); + + for (int i = 0; i < worn.worn.length; i++) { + String s = worn.worn[i]; + if (s != null && !s.equals("none")) + items.add(new ItemInfo(s, i)); + } + for (String s : worn.all) { + items.add(new ItemInfo(s, -1)); + } + + list.getItems().setAll(items); + } +} diff --git a/DuskZ/src/duskz/client/fx/MainFrameFX.java b/DuskZ/src/duskz/client/fx/MainFrameFX.java new file mode 100644 index 0000000..be60d02 --- /dev/null +++ b/DuskZ/src/duskz/client/fx/MainFrameFX.java @@ -0,0 +1,992 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2000 Tom Weingarten + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/** + * Changes + * Feb-2013 Michael Zucchi - This is mostly new apart from + * a bit of the keyboard and mouse handler code and the structure of the + * rendering pass. + */ +package duskz.client.fx; + +import duskz.client.Bookmarks; +import duskz.client.ClientMap; +import duskz.client.Direction; +import duskz.client.Dusk; +import duskz.client.Entity; +import duskz.client.Equipment; +import duskz.client.GUI; +import duskz.client.Status; +import duskz.client.TransactionItem; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Random; +import java.util.logging.Level; +import java.util.logging.Logger; +import javafx.animation.FadeTransition; +import javafx.animation.FadeTransitionBuilder; +import javafx.animation.Interpolator; +import javafx.animation.ParallelTransition; +import javafx.animation.Transition; +import javafx.animation.TranslateTransitionBuilder; +import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.geometry.HPos; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.geometry.Rectangle2D; +import javafx.geometry.VPos; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ListView; +import javafx.scene.control.MenuButton; +import javafx.scene.control.MenuItem; +import javafx.scene.control.MenuItemBuilder; +import javafx.scene.control.PasswordField; +import javafx.scene.control.Separator; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.scene.effect.BoxBlur; +import javafx.scene.effect.DropShadow; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import static javafx.scene.input.KeyCode.DOWN; +import static javafx.scene.input.KeyCode.LEFT; +import static javafx.scene.input.KeyCode.RIGHT; +import static javafx.scene.input.KeyCode.UP; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; +import javafx.scene.text.Text; +import javafx.util.Duration; + +/** + * This is the main JavaFX window. + * + * It handles all the GUI related operations. + * + * Prototype code herein, it needs to be split out into functional windows to + * make it more manageable + * + * @author notzed + */ +public class MainFrameFX extends StackPane implements GUI { + + Dusk game; + // + BorderPane main; + StackPane layers; + Pane graphics; + Pane battle; + BorderPane panel; + VBox buttons; + TextArea stats; + TextArea log; + TextField cmdLine; + Label info; + // just clone interface to start with + MenuButton attack, action, look, get, drop; + // + Button connect, merchant, equipment, quit; + /** + * ************** + * Behaviour state + */ + /** + * "!command" allows one to execute a fixed operation after every move. + */ + private String postMove; + + public void setGame(Dusk game) { + this.game = game; + } + + public MainFrameFX() { + layers = new StackPane(); + + getChildren().add(main = new BorderPane()); + + layers.getChildren().addAll(graphics = new Pane(), battle = new Pane()); + + layers.setPrefSize(416, 416); + main.setCenter(layers); + + //layers.setScaleX(2); + //layers.setScaleY(2); + + panel = new BorderPane(); + panel.setMaxWidth(320); + main.setRight(panel); + + buttons = new VBox(); + stats = new TextArea(); + panel.setTop(info = new Label()); + panel.setLeft(buttons); + panel.setCenter(stats); + + buttons.getChildren().addAll( + attack = new MenuButton("Attack"), + action = new MenuButton("Action"), + look = new MenuButton("Look"), + get = new MenuButton("Get"), + drop = new MenuButton("Drop"), + connect = new Button("Connect"), + merchant = new Button("Merchant"), + equipment = new Button("Equipment"), + quit = new Button("Quit")); + + attack.setMaxWidth(Double.MAX_VALUE); + action.setMaxWidth(Double.MAX_VALUE); + look.setMaxWidth(Double.MAX_VALUE); + get.setMaxWidth(Double.MAX_VALUE); + drop.setMaxWidth(Double.MAX_VALUE); + connect.setMaxWidth(Double.MAX_VALUE); + merchant.setMaxWidth(Double.MAX_VALUE); + equipment.setMaxWidth(Double.MAX_VALUE); + quit.setMaxWidth(Double.MAX_VALUE); + + BorderPane lower = new BorderPane(); + main.setBottom(lower); + + lower.setTop(cmdLine = new TextField()); + lower.setBottom(log = new TextArea()); + + // Events + cmdLine.setOnKeyPressed(keyPressed); + layers.setOnKeyPressed(keyPressed); + layers.setOnMouseClicked(mouseClicked); + + merchant.setOnAction(new EventHandler() { + public void handle(javafx.event.ActionEvent t) { + game.merchant(); + } + }); + quit.setOnAction(new EventHandler() { + public void handle(javafx.event.ActionEvent t) { + game.quit(); + } + }); + equipment.setOnAction(new EventHandler() { + public void handle(javafx.event.ActionEvent t) { + game.equipment(); + } + }); + connect.setOnAction(new EventHandler() { + public void handle(javafx.event.ActionEvent t) { + //userconnect(); + } + }); + } + Random rand = new Random(); + + public void damageEntity(Entity e, double direction, double x, double y, int delta, String what) { + final Label node = new Label(String.format("%s %+d", what, delta)); + + if (delta <= 0) { + node.setId("damage-bubble"); + } else { + node.setId("health-bubble"); + } + node.relocate(x, y); + + Duration dur = Duration.millis(1500); + // Fade out + Transition fade = + FadeTransitionBuilder.create().node(node) + .duration(dur) + .interpolator(Interpolator.EASE_IN) + .fromValue(1) + .toValue(0) + .onFinished(new EventHandler() { + @Override + public void handle(ActionEvent t) { + battle.getChildren().remove(node); + } + }).build(); + + // animate randomly away from centre + double a = rand.nextDouble() * Math.PI * 0.75 + direction - Math.PI * 0.375; + + //a = direction; + + double radius = 32; + double sx = Math.cos(a) * radius / 2; + double sy = Math.sin(a) * radius / 2; + double ex = Math.cos(a) * radius + sx; + double ey = Math.sin(a) * radius + sy; + + Transition move = + TranslateTransitionBuilder.create().node(node) + .duration(dur) + .interpolator(Interpolator.EASE_OUT) + .fromX(sx).fromY(sy) + .toX(ex).toY(ey) + .build(); + + final Transition t = new ParallelTransition(fade, move); + + // Quick and dirty 'damage' popup + Platform.runLater(new Runnable() { + @Override + public void run() { + battle.getChildren().add(node); + + t.play(); + } + }); + } + Bookmarks bm; + GridPane loginPane; + ListView marks; + TextField host; + TextField port; + TextField user; + TextField pass; + + public void showLogin() { + if (loginPane != null) { + return; + } + + loginPane = new GridPane(); + loginPane.setId("login-window"); + loginPane.setPrefSize(320, 200); + loginPane.setMaxSize(500, 300); + StackPane.setAlignment(loginPane, Pos.CENTER); + + bm = new Bookmarks(); + try { + bm.load("bookmarks"); + } catch (FileNotFoundException x) { + } catch (IOException ex) { + Logger.getLogger(MainFrameFX.class.getName()).log(Level.SEVERE, null, ex); + } + + marks = new ListView<>(); + + marks.getItems().setAll(bm.getBookmarks()); + Label l; + Insets li = new Insets(4, 8, 4, 8); + loginPane.add(marks, 0, 0, 1, 7); + + loginPane.add(l = new Label("Host"), 1, 0); + GridPane.setMargin(l, li); + loginPane.add(host = new TextField(), 2, 0); + loginPane.add(l = new Label("Port"), 1, 1); + GridPane.setMargin(l, li); + loginPane.add(port = new TextField(), 2, 1); + + GridPane.setHgrow(host, Priority.ALWAYS); + GridPane.setHgrow(port, Priority.ALWAYS); + + Separator empty = new Separator(); + GridPane.setVgrow(empty, Priority.ALWAYS); + loginPane.add(empty, 1, 2, 2, 1); + + HBox hbox; + loginPane.add(hbox = new HBox(), 1, 3, 2, 1); + hbox.setAlignment(Pos.CENTER); + //GridPane.setHalignment(hbox, HPos.CENTER); + Button add = new Button("Add"); + Button remove = new Button("Remove"); + + hbox.getChildren().addAll(add, remove); + + loginPane.add(l = new Label("User"), 1, 4); + GridPane.setMargin(l, li); + loginPane.add(user = new TextField(), 2, 4); + loginPane.add(l = new Label("Pass"), 1, 5); + GridPane.setMargin(l, li); + loginPane.add(pass = new PasswordField(), 2, 5); + + Button login = new Button("Connect"); + + GridPane.setValignment(login, VPos.BOTTOM); + GridPane.setHalignment(login, HPos.CENTER); + loginPane.add(login, 1, 6, 2, 1); + + + GridPane.setVgrow(hbox, Priority.NEVER); + GridPane.setVgrow(login, Priority.NEVER); + + bm.setBookmarks(marks.getItems()); + + marks.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { + public void changed(ObservableValue ov, Bookmarks.Bookmark t, Bookmarks.Bookmark t1) { + host.setText(t1.host); + port.setText(Integer.toString(t1.port)); + } + }); + + add.setOnAction(new EventHandler() { + public void handle(ActionEvent t) { + try { + marks.getItems().add(new Bookmarks.Bookmark(host.getText(), Integer.valueOf(port.getText()))); + bm.save("bookmarks"); + } catch (NumberFormatException x) { + } catch (IOException x) { + } + } + }); + + remove.setOnAction(new EventHandler() { + public void handle(ActionEvent t) { + int i = marks.getSelectionModel().getSelectedIndex(); + try { + + if (i != -1) { + marks.getItems().remove(i); + bm.save("bookmarks"); + } + } catch (IOException ex) { + Logger.getLogger(MainFrameFX.class.getName()).log(Level.SEVERE, null, ex); + } + } + }); + + login.setOnAction(new EventHandler() { + public void handle(ActionEvent t) { + System.out.println("logging in!"); + game.connect(host.getText(), Integer.valueOf(port.getText()), user.getText(), pass.getText()); + } + }); + + loginPane.setOpacity(0); + getChildren().add(loginPane); + + FadeTransition anim = new FadeTransition(Duration.millis(250), loginPane); + anim.setFromValue(0); + anim.setToValue(1); + anim.play(); + } + + public void loginFailed() { + // display somewhere? + } + + public void loginOk() { + if (loginPane != null) { + closeWindow(loginPane); + loginPane = null; + } + } + + void closeWindow(final Pane window) { + Platform.runLater(new Runnable() { + public void run() { + main.setEffect(null); + if (true) { + final FadeTransition anim = new FadeTransition(Duration.millis(250), window); + + anim.setFromValue(1); + anim.setToValue(0); + anim.setOnFinished(new EventHandler() { + public void handle(ActionEvent t) { + getChildren().remove(anim.getNode()); + } + }); + anim.play(); + } else { + getChildren().remove(window); + } + } + }); + } + GridPane racePane; + ListView raceList; + + public void chooseRace(String prompt, List races) { + if (racePane != null) { + return; + } + + racePane = new GridPane(); + racePane.setId("race-window"); + racePane.setMaxSize(500, 300); + StackPane.setAlignment(racePane, Pos.CENTER); + + raceList = new ListView<>(); + raceList.getItems().setAll(races); + + racePane.add(new Label(prompt), 0, 0); + + racePane.add(raceList, 0, 1); + + Button ok = new Button("Ok"); + + racePane.add(ok, 0, 2); + + ok.setOnAction(new EventHandler() { + public void handle(ActionEvent t) { + String item = raceList.getSelectionModel().getSelectedItem(); + if (item != null) { + getChildren().remove(racePane); + racePane = null; + game.chooseRace(item); + } + } + }); + + Platform.runLater(new Runnable() { + public void run() { + getChildren().add(racePane); + } + }); + + } + /** + * Need to know about merchant changes so track them here. + */ + VBox shopPane; + TransactionPane buyPane; + TransactionPane sellPane; + + public void setBuyList(final List buyList) { + Platform.runLater(new Runnable() { + public void run() { + if (shopPane != null) { + buyPane.setItems(buyList); + } + merchant.setDisable(false); + } + }); + } + + public void setSellList(final List sellList) { + Platform.runLater(new Runnable() { + public void run() { + if (shopPane != null) { + sellPane.setItems(sellList); + } + merchant.setDisable(false); + } + }); + } + + @Override + public void exitShop() { + merchant.setDisable(!false); + if (shopPane != null) { + closeWindow(shopPane); + shopPane = null; + } + } + + @Override + public void enterShop(String title, final List buyList, final List sellList) { + if (shopPane != null) { + // update items? + Platform.runLater(new Runnable() { + public void run() { + buyPane.setItems(buyList); + sellPane.setItems(sellList); + } + }); + + return; + } + + shopPane = new VBox(); + shopPane.setId("shop-window"); + shopPane.setMaxSize(600, 400); + StackPane.setAlignment(shopPane, Pos.CENTER); + + TabPane tabs = new TabPane(); + + Tab tab = new Tab("Buy"); + Button b; + + buyPane = new TransactionPane(b = new Button("Buy")); + tab.setContent(buyPane); + tab.setClosable(false); + tabs.getTabs().add(tab); + + b.setOnAction(new EventHandler() { + public void handle(ActionEvent t) { + game.buy(buyPane.getItem(), buyPane.getQuantity()); + } + }); + + tab = new Tab("Sell"); + sellPane = new TransactionPane(b = new Button("Sell")); + tab.setContent(sellPane); + tab.setClosable(false); + tabs.getTabs().add(tab); + + b.setOnAction(new EventHandler() { + public void handle(ActionEvent t) { + game.sell(sellPane.getItem(), sellPane.getQuantity()); + } + }); + + Label head = new Label(title); + head.setId("window-title"); + head.setAlignment(Pos.BASELINE_LEFT); + + buyPane.setItems(buyList); + sellPane.setItems(sellList); + + Button tail = new Button("Leave"); + + tail.setOnAction(new EventHandler() { + public void handle(ActionEvent t) { + closeWindow(shopPane); + shopPane = null; + } + }); + + shopPane.setAlignment(Pos.CENTER); + shopPane.getChildren().addAll(head, tabs, tail); + + Platform.runLater(new Runnable() { + public void run() { + main.setEffect(new BoxBlur(3, 3, 2)); + getChildren().add(shopPane); + } + }); + } + VBox equipWindow; + EquipmentPane equip; + + @Override + public void manageEquipment(Equipment worn) { + if (equipWindow != null) { + equip.setEquipment(worn); + return; + } + + equipWindow = new VBox(); + equipWindow.setId("shop-window"); + equipWindow.setMaxSize(600, 400); + StackPane.setAlignment(equipWindow, Pos.CENTER); + + equip = new EquipmentPane(); + + Label head = new Label("Equipment"); + head.setId("window-title"); + head.setAlignment(Pos.BASELINE_LEFT); + + equip.setEquipment(worn); + + Button tail = new Button("Return"); + + tail.setOnAction(new EventHandler() { + public void handle(ActionEvent t) { + closeWindow(equipWindow); + equipWindow = null; + } + }); + + equipWindow.setAlignment(Pos.CENTER); + equipWindow.getChildren().addAll(head, equip, tail); + + equip.wear.setOnAction(new EventHandler() { + public void handle(ActionEvent t) { + game.wear(equip.getItem().name); + } + }); + equip.unwear.setOnAction(new EventHandler() { + public void handle(ActionEvent t) { + EquipmentPane.ItemInfo ii = equip.getItem(); + game.unwear(Equipment.names[ii.wornAt]); + } + }); + equip.drop.setOnAction(new EventHandler() { + public void handle(ActionEvent t) { + game.drop(equip.getItem().name); + } + }); + + Platform.runLater(new Runnable() { + public void run() { + main.setEffect(new BoxBlur(3, 3, 2)); + getChildren().add(equipWindow); + } + }); + + + } + + /** + * For closing poup windows that can be closed + */ + public void exitWindow() { + if (shopPane != null) { + closeWindow(shopPane); + shopPane = null; + } + } + + @Override + public void visitFile(String file, String text, boolean canSave) { + throw new UnsupportedOperationException("Not supported yet."); + // FIXME: implement edit text, + // FIXME: sent using: appParent.outstream.writeBytes("submit "+strName+"\n"); + // appParent.outstream.writeBytes(txtEdit.getText()+"\n--EOF--\n"); + } + //Accept key input + EventHandler keyPressed = new EventHandler() { + @Override + public void handle(javafx.scene.input.KeyEvent evt) { + if (!game.isConnected()) { + return; + } + + KeyCode nkey = evt.getCode(); + if (nkey == KeyCode.ENTER) { + String cmd = cmdLine.getText(); + //Thanks to Joe Alloway for this addition + if (cmd.startsWith("!")) { + if (cmd.length() == 1) { + postMove = null; + } else { + postMove = cmd.substring(1); + } + } else { + game.command(cmd); + } + //End contribution by Joe Alloway + cmdLine.setText(""); + } + if (game.isLoaded()) { + Direction dir = null; + switch (nkey) { + case UP: + dir = Direction.North; + break; + case DOWN: + dir = Direction.South; + break; + case LEFT: + dir = Direction.West; + break; + case RIGHT: + dir = Direction.East; + break; + } + if (dir != null) { + game.move(dir); + if (postMove != null) + game.command(postMove); + } + } + } + }; + EventHandler mouseClicked = new EventHandler() { + @Override + public void handle(javafx.scene.input.MouseEvent evt) { + System.out.println("Mouse clicked: " + evt); + if (!game.isConnected() || !game.isLoaded()) { + return; + } + + /** + * Map coordinates to screen-relative tiles and pass to game. + */ + int x = (int) evt.getX(); + int y = (int) evt.getY(); + int destX = (x / game.getTileSize()) + game.getMapOffsetX(); + int destY = (y / game.getTileSize()) + game.getMapOffsetY(); + + game.moveTo(destX, destY); + cmdLine.requestFocus(); + } + }; + EventHandler attackHandler = new EventHandler() { + public void handle(ActionEvent t) { + game.attack((Entity) ((MenuItem) t.getSource()).getUserData()); + } + }; + EventHandler lookHandler = new EventHandler() { + public void handle(ActionEvent t) { + game.look((Entity) ((MenuItem) t.getSource()).getUserData()); + } + }; + EventHandler actionHandler = new EventHandler() { + public void handle(ActionEvent t) { + MenuItem mi = (MenuItem) t.getSource(); + game.command(mi.getText()); + } + }; + EventHandler getHandler = new EventHandler() { + public void handle(ActionEvent t) { + game.take((Entity) ((MenuItem) t.getSource()).getUserData()); + } + }; + EventHandler dropHandler = new EventHandler() { + public void handle(ActionEvent t) { + game.drop(((MenuItem) t.getSource()).getText()); + } + }; + + @Override + public void logClear() { + Platform.runLater(new Runnable() { + public void run() { + log.setText(""); + } + }); + } + + @Override + public GUI log(String txt, Object... args) { + final String s = String.format(txt, args); + + Platform.runLater(new Runnable() { + public void run() { + + // Truncate history + if (log.textProperty().getValueSafe().length() > 8000) { + log.deleteText(0, 4000); + } + + log.appendText(s); + } + }); + return this; + } + + void setStringMenu(final MenuButton menu, List list, EventHandler handle) { + final List items = new ArrayList<>(); + MenuItemBuilder mib = MenuItemBuilder.create().onAction(handle); + + for (String e : list) { + items.add(mib.text(e).build()); + } + Platform.runLater(new Runnable() { + @Override + public void run() { + menu.getItems().setAll(items); + } + }); + } + + void setEntityMenu(final MenuButton menu, List list, EventHandler handle) { + final List items = new ArrayList<>(); + MenuItemBuilder mib = MenuItemBuilder.create().onAction(handle); + + for (Entity e : list) { + items.add(mib.userData(e).text(e.getIndexedName()).build()); + } + Platform.runLater(new Runnable() { + public void run() { + menu.getItems().setAll(items); + } + }); + } + + @Override + public void setDropList(List drops) { + setStringMenu(drop, drops, dropHandler); + } + + @Override + public void setAttackList(List list) { + setEntityMenu(attack, list, attackHandler); + } + + @Override + public void setTakeList(List list) { + setEntityMenu(get, list, getHandler); + } + + @Override + public void setLookList(List list) { + setEntityMenu(look, list, lookHandler); + } + + @Override + public void setActionList(List list) { + setStringMenu(action, list, actionHandler); + } + + @Override + public void setStats(final String stats) { + Platform.runLater(new Runnable() { + public void run() { + MainFrameFX.this.stats.setText(stats); + } + }); + } + + @Override + public void setStatus(final Status status) { + Platform.runLater(new Runnable() { + public void run() { + info.setText(status.toString()); + } + }); + } + + @Override + public void setEquipment(final Equipment worn) { + Platform.runLater(new Runnable() { + public void run() { + if (equipWindow != null) { + equip.setEquipment(worn); + } + } + }); + } + //DataManagerFX data; + Image tileImage; + int tileSize; + Image playerImage; + int playerSize; + Image spriteImage; + int spriteSize; + + @Override + public void setImages(String tiles, int tileSize, String players, int playerSize, String sprites, int spriteSize) { + //try { + System.out.println("set tile image"); + // FIXME: put sprites into data manager + //data = new DataManagerFX("/home/notzed/house.jar"); + //data.open(); + tileImage = new Image(tiles, false); + this.tileSize = tileSize; + playerImage = new Image(players, false); + this.playerSize = playerSize; + spriteImage = new Image(sprites, false); + this.spriteSize = spriteSize; + //} catch (IOException ex) { + // Logger.getLogger(MainFrameFX.class.getName()).log(Level.SEVERE, null, ex); + //} + } + + @Override + public void updateMap(ClientMap map) { + // First ugly cut - just create a whole set of tiles + System.out.println("update map"); + //if (data == null) { + if (tileImage == null) { + System.out.println("Map not ready yet"); + return; + } + + final ArrayList children = new ArrayList<>(); + final ArrayList upper = new ArrayList<>(); + + // Build map + for (int y = 0; y < map.rows; y++) { + for (int x = 0; x < map.cols; x++) { + ImageView iv = new ImageView(tileImage); + int tileid = map.getTile(x, y); + iv.setViewport(new Rectangle2D(tileid * tileSize, 0, tileSize, tileSize)); + iv.relocate(x * tileSize, y * tileSize); + children.add(iv); + + //children.add(data.createTile(map.getTile(x, y), x, y, tileSize, tileSize)); + + // TODO: names always? on top + Collection ents = map.getEntities(x + map.offx, y + map.offy); + if (ents != null) { + for (Entity e : ents) { + drawEntity(e, map.offx, map.offy, children, upper); + } + } + } + } + + // Build sprites + //for (Entity e : map.getEntities()) { + // drawEntity(map.offx, map.offy, children, e); + //} + + Platform.runLater(new Runnable() { + @Override + public void run() { + graphics.getChildren().setAll(children); + graphics.getChildren().addAll(upper); + } + }); + } + DropShadow textShadow = new DropShadow(3, 2, 2, Color.BLACK); + + // HACK: upper is used for 'upper layer', stuff drawn afterwards + void drawEntity(Entity e, int offx, int offy, ArrayList children, ArrayList upper) { + double x = e.locx - offx; + double y = e.locy - offy; + + // TODO: just make it an entity node + + if (e.intStep == -1) { + // Hack: sprites are 64x64 + ImageView iv = new ImageView(spriteImage); + iv.setViewport(new Rectangle2D(e.intImage * spriteSize, 0, + spriteSize, spriteSize)); + iv.relocate((x * tileSize) - tileSize / 2, (y * tileSize) - tileSize / 2); + iv.setScaleX(0.5); + iv.setScaleY(0.5); + children.add(iv); + + } else { + ImageView iv = new ImageView(playerImage); + iv.setViewport(new Rectangle2D((e.intImage * 8 + e.intStep) * spriteSize, 0, + spriteSize, spriteSize)); + iv.relocate((x * tileSize) - tileSize / 2, (y * tileSize) - tileSize / 2); + iv.setScaleX(0.5); + iv.setScaleY(0.5); + children.add(iv); + } + if (e.intNum == 0) { + Text t = new Text(e.strName); + t.setId("entity-label"); + t.relocate((x * tileSize) + tileSize / 2 - t.getLayoutBounds().getWidth() / 2, ((y + 1) * tileSize)); + upper.add(t); + } else { + Text t = new Text(e.intNum + "." + e.strName); + t.setId("entity-label"); + t.relocate((x * tileSize) + tileSize / 2 - t.getLayoutBounds().getWidth() / 2, ((y + 1) * tileSize)); + upper.add(t); + } + //Draw flag + if (e.intFlag != 0) { + Rectangle r = new Rectangle(1, 1, tileSize - 2, tileSize - 2); + if (e.intFlag == 1) { + r.setStroke(Color.GREEN); + } else if (e.intFlag == 2) { + r.setStroke(Color.RED); + } + r.setStrokeWidth(2); + r.setArcHeight(12); + r.setArcWidth(12); + r.setFill(null); + r.relocate(x * tileSize, y * tileSize); + upper.add(r); + } + } +} diff --git a/DuskZ/src/duskz/client/fx/TransactionPane.java b/DuskZ/src/duskz/client/fx/TransactionPane.java new file mode 100644 index 0000000..733bdf9 --- /dev/null +++ b/DuskZ/src/duskz/client/fx/TransactionPane.java @@ -0,0 +1,137 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ. If not, see . + */ +/** + * Changes + */ +package duskz.client.fx; + +import duskz.client.TransactionItem; +import java.util.List; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import jfxtras.labs.scene.control.ListSpinner; + +/** + * + * @author notzed + */ +public class TransactionPane extends HBox { + + TableView table; + Label detail; + ListSpinner quantity; + Button action; + + public TransactionPane(Button action) { + this.action = action; + + setId("buy-pane"); + + VBox right = new VBox(); + + HBox.setHgrow(right, Priority.ALWAYS); + + right.setAlignment(Pos.TOP_RIGHT); + right.setFillWidth(true); + + table = new TableView<>(); + detail = new Label(); + quantity = new ListSpinner(1, 100); + + HBox hb; + + hb = new HBox(); + hb.setFillHeight(true); + VBox.setVgrow(hb, Priority.ALWAYS); + hb.getChildren().add(detail); + right.getChildren().add(hb); + + hb = new HBox(); + hb.getChildren().add(quantity); + hb.getChildren().add(new Label("Quantity:")); + hb.setAlignment(Pos.CENTER); + right.getChildren().add(hb); + + // hb = new HBox(); + // hb.getChildren().add(action); + // hb.setAlignment(Pos.BOTTOM_RIGHT); + // right.getChildren().add(hb); + right.getChildren().add(action); + + HBox.setHgrow(table, Priority.NEVER); + + getChildren().addAll(table, right); + + + TableColumn countCol = new TableColumn("Count"); + countCol.setCellValueFactory(new PropertyValueFactory("count")); + + TableColumn nameCol = new TableColumn("Name"); + nameCol.setCellValueFactory(new PropertyValueFactory("name")); + + TableColumn costCol = new TableColumn("Cost"); + costCol.setCellValueFactory(new PropertyValueFactory("cost")); + + table.getColumns().setAll(countCol, nameCol, costCol); + + countCol.setMinWidth(50); + nameCol.setMinWidth(180); + costCol.setMinWidth(50); + table.setMinWidth(310); + table.setMaxWidth(310); + + countCol.setResizable(false); + nameCol.setResizable(false); + costCol.setResizable(false); + table.setEditable(false); + + table.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue ov, TransactionItem t, TransactionItem t1) { + if (t1 != null) { + detail.setText(String.format("Item: %s\nCost: %s\n\n ... some description or shit ...\n", + t1.name, t1.cost)); + } else { + detail.setText(""); + } + } + }); + } + + public int getQuantity() { + return ((Number) quantity.getValue()).intValue(); + } + + public TransactionItem getItem() { + return table.getSelectionModel().getSelectedItem(); + } + + public void setItems(List items) { + this.table.getItems().setAll(items); + } +} diff --git a/DuskZ/src/duskz/client/fx/style.css b/DuskZ/src/duskz/client/fx/style.css new file mode 100644 index 0000000..c81c59b --- /dev/null +++ b/DuskZ/src/duskz/client/fx/style.css @@ -0,0 +1,85 @@ +/* + Document : style + Created on : 21/02/2013, 11:27:00 AM + Author : notzed + Description: A very ugly stylesheet just waiting for a graphical designer + to improve it. +*/ + +.scene { + /* global palette */ + enitity-background: rgba(1,1,0.5,0.25); +} +.button { + -fx-background-color: + #c3c4c4, + linear-gradient(#d6d6d6 50%, white 100%), + radial-gradient(center 50% -40%, radius 200%, #e6e6e6 45%, rgba(230,230,230,0) 50%); + /* -fx-background-radius: 30;*/ + -fx-background-insets: 0,1,1; + -fx-text-fill: black; + -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 3, 0.0 , 0 , 1 ); +} + +#login-window, #race-window, #shop-window { + -fx-padding: 10; + -fx-border-color: #000000; + -fx-border-radius: 12; + -fx-background-radius: 12; + -fx-background-color: + linear-gradient(to bottom right, antiquewhite, maroon); +} + +#entity-label { + -fx-font-family: 'sans'; + -fx-font-size: small; + -fx-font-weight: bold; + -fx-fill: white; + -fx-effect: dropshadow(one-pass-box, black, 4, 0, 2, 2); +} +#window-title { + -fx-font-family: 'serif'; + -fx-font-size: x-large; +} +.tab-pane { + -fx-tab-min-width: 250px; +} + +.tab-header-area { + -fx-alignment: center; +} + +#shop-window .table-view { + -fx-padding: 5px; +} +#shop-window .table-cell { + -fx-font-size: small; + -fx-padding: 0px; +} + +.table-row-cell, .list-cell { + -fx-background-color: lemonchiffon; +} +.table-row-cell:selected, .list-cell:selected { + -fx-background-color: burlywood; +} +#shop-window .label { + -fx-padding: 5px; +} +#health-bubble, #damage-bubble { + -fx-background-radius: 20; + -fx-background-insets: 0; + -fx-text-fill: black; + -fx-padding: 4; +} + +#health-bubble { + -fx-background-color: #33c333, + linear-gradient(#33c333 50%, white 100%), + radial-gradient(center 50% -40%, radius 200%, #e6e6e6 45%, rgba(230,230,230,0) 50%); +} +#damage-bubble { + -fx-background-color: #c33333, + linear-gradient(#c33333 50%, white 100%), + radial-gradient(center 50% -40%, radius 200%, #e6e6e6 45%, rgba(230,230,230,0) 50%); +} diff --git a/DuskZ/src/jfxtras/labs/animation/Timer.java b/DuskZ/src/jfxtras/labs/animation/Timer.java new file mode 100644 index 0000000..f7a56fd --- /dev/null +++ b/DuskZ/src/jfxtras/labs/animation/Timer.java @@ -0,0 +1,155 @@ +/** + * Copyright (c) 2011, JFXtras + * 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 the 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 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. + */ +package jfxtras.labs.animation; + +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicReference; + +import javafx.application.Platform; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.util.Duration; + +/** + * A timer class in the spirit of java.swing.Timer but using JavaFX properties. + * + * @author Tom Eugelink + * + */ +public class Timer +{ + // ================================================================================================================== + // CONSTRUCTOR + + /** + * + * @param runnable + */ + public Timer(Runnable runnable) + { + this(true, runnable); + } + + /** + * + * @param isDaemon + * @param runnable + */ + public Timer(boolean isDaemon, final Runnable runnable) + { + this.runnable = runnable; + timer = new java.util.Timer(isDaemon); + } + final private Runnable runnable; + final private java.util.Timer timer; + + // ================================================================================================================== + // PROPERTIES + + /** delay: initial delay */ + public ObjectProperty delayProperty() { return this.delayObjectProperty; } + final private ObjectProperty delayObjectProperty = new SimpleObjectProperty(this, "delay", Duration.millis(0)); + public Duration getDelay() { return this.delayObjectProperty.getValue(); } + public void setDelay(Duration value) { this.delayObjectProperty.setValue(value); } + public Timer withDelay(Duration value) { setDelay(value); return this; } + + /** cycleDuration: time between fires */ + public ObjectProperty cycleDurationProperty() { return this.cycleDurationObjectProperty; } + final private ObjectProperty cycleDurationObjectProperty = new SimpleObjectProperty(this, "cycleDuration", Duration.millis(1000)); + public Duration getCycleDuration() { return this.cycleDurationObjectProperty.getValue(); } + public void setCycleDuration(Duration value) { this.cycleDurationObjectProperty.setValue(value); } + public Timer withCycleDuration(Duration value) { setCycleDuration(value); return this; } + + /** repeats: If flag is false, instructs the Timer to send only one action event to its listeners. */ + public ObjectProperty repeatsProperty() { return this.repeatsObjectProperty; } + final private ObjectProperty repeatsObjectProperty = new SimpleObjectProperty(this, "repeats", Boolean.TRUE); + public boolean getRepeats() { return this.repeatsObjectProperty.getValue(); } + public void setRepeats(boolean value) { this.repeatsObjectProperty.setValue(value); } + public Timer withRepeats(boolean value) { setRepeats(value); return this; } + + + // ================================================================================================================== + // TIMER + + /** + * Start the timer + */ + synchronized public Timer start() + { + // check if the timer is already running + if (timerTaskAtomicReference.get() != null) throw new IllegalStateException("Timer already started"); + + // create a task and schedule it + final TimerTask lTimerTask = new TimerTask() + { + @Override + public void run() + { + Platform.runLater(runnable); + if (repeatsObjectProperty.getValue().booleanValue() == false) + { + stop(); + } + } + }; + timer.schedule(lTimerTask, (long)this.delayObjectProperty.getValue().toMillis(), (long)this.cycleDurationObjectProperty.getValue().toMillis()); + + // remember for future reference + timerTaskAtomicReference.set(lTimerTask); + + // for chaining + return this; + } + final private AtomicReference timerTaskAtomicReference = new AtomicReference(null); + + /** + * stop the timer if running + */ + public Timer stop() + { + TimerTask lTimerTask = timerTaskAtomicReference.getAndSet(null); + if (lTimerTask != null) + { + lTimerTask.cancel(); + } + + // for chaining + return this; + } + + /** + * restart the timer + */ + public Timer restart() + { + stop(); + start(); + + // for chaining + return this; + } +} diff --git a/DuskZ/src/jfxtras/labs/internal/scene/control/ListSpinner.css b/DuskZ/src/jfxtras/labs/internal/scene/control/ListSpinner.css new file mode 100644 index 0000000..cfeb60d --- /dev/null +++ b/DuskZ/src/jfxtras/labs/internal/scene/control/ListSpinner.css @@ -0,0 +1,47 @@ +/* basic settings */ +.ListSpinner { + -fx-skin: "jfxtras.labs.internal.scene.control.skin.ListSpinnerCaspianSkin"; + -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color; + -fx-background-insets: 0 0 -1 0, 0, 1, 2; + -fx-background-radius: 5, 5, 4, 3; + -fx-padding: 0.266667em 0.233333em 0.25em 0.233333em; + -fx-text-fill: -fx-text-base-color; +} + +.ListSpinner:hover { + -fx-color: -fx-hover-base; +} + +.ListSpinner:focused { + -fx-color: -fx-focused-base; + -fx-background-color: -fx-focus-color, -fx-outer-border, -fx-inner-border, -fx-body-color; + -fx-background-insets: -1.4, 0, 1, 2; + -fx-background-radius: 6.4, 5, 4, 3; +} + +.ListSpinner .value { + -fx-padding: 0.0em 0.2em 0.0em 0.2em; +} + +.ListSpinner .left-arrow { + -fx-shape: "M4,-4 L0,0 L4,4 Z"; + -fx-scale-shape: false; +} +.ListSpinner .right-arrow { + -fx-shape: "M0,-4 L4,0 L0,4 Z"; + -fx-scale-shape: false; +} +.ListSpinner .down-arrow { + -fx-shape: "M-4,-2 L0,2 L4,-2 Z"; + -fx-scale-shape: false; +} +.ListSpinner .up-arrow { + -fx-shape: "M4,2 L-4,2 L0,-2 Z"; + -fx-scale-shape: false; +} +.ListSpinner .idle { + -fx-background-color: -fx-mark-color; +} +.ListSpinner .clicked { + -fx-background-color: -fx-focus-color; +} diff --git a/DuskZ/src/jfxtras/labs/internal/scene/control/behavior/ListSpinnerBehavior.java b/DuskZ/src/jfxtras/labs/internal/scene/control/behavior/ListSpinnerBehavior.java new file mode 100644 index 0000000..99e6cf8 --- /dev/null +++ b/DuskZ/src/jfxtras/labs/internal/scene/control/behavior/ListSpinnerBehavior.java @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2011, JFXtras + * 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 the 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 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. + */ +package jfxtras.labs.internal.scene.control.behavior; + +import java.util.ArrayList; +import java.util.List; + +import javafx.scene.input.KeyCode; +import javafx.scene.input.MouseEvent; +import javafx.util.Callback; +import jfxtras.labs.scene.control.ListSpinner; + +import com.sun.javafx.scene.control.behavior.BehaviorBase; +import com.sun.javafx.scene.control.behavior.KeyBinding; + +/** + * + * @author Tom Eugelink + * + */ +public class ListSpinnerBehavior extends BehaviorBase> +{ + // ================================================================================================================== + // CONSTRUCTOR + + /** + * + * @param control + */ + public ListSpinnerBehavior(ListSpinner control) + { + super(control); + construct(); + } + + /* + * + */ + private void construct() + { + + } + + // ================================================================================================================== + // EDITABLE + + /** + * Parse the value (which usually comes from the TextField in the skin). + * If the value exists in the current items, select it. + * If not and a callback is defined, call the callback to have it handle it. + * Otherwise do nothing (leave it to the skin). + */ + public void parse(String text) + { + // strip + String lText = text; + String lPostfix = getControl().getPostfix(); + if (lPostfix.length() > 0 && lText.endsWith(lPostfix)) lText = lText.substring(0, lText.length() - lPostfix.length()); + String lPrefix = getControl().getPrefix(); + if (lPrefix.length() > 0 && lText.startsWith(lPrefix)) lText = lText.substring(lPrefix.length()); + + // convert from string to value + T lValue = getControl().getStringConverter().fromString(lText); + + // if the value does exists in the domain + int lItemIndex = getControl().getItems().indexOf(lValue); + if (lItemIndex >= 0) + { + // accept value and bail out + getControl().setValue(lValue); + return; + } + + // check to see if we have a addCallback + Callback lAddCallback = getControl().getAddCallback(); + if (lAddCallback != null) + { + // call the callback + Integer lIndex = lAddCallback.call(lValue); + + // if the callback reports that it has processed the value by returning the index where it has added the item. (Or at least the index it wants to show now.) + if (lIndex != null) + { + // accept value and bail out + getControl().setIndex(lIndex); + return; + } + } + } + + // ================================================================================================================== + // MOUSE EVENTS + + /** + * + */ + @Override public void mousePressed(MouseEvent evt) + { + // get the control + ListSpinner lControl = getControl(); + + // if a control does not have the focus, request focus + if (!lControl.isFocused() && lControl.isFocusTraversable()) { + lControl.requestFocus(); + } + } + + // ================================================================================================================== + // KEY EVENTS + + final static private String EVENT_PREVIOUS = "PreviousPressed"; + final static private String EVENT_NEXT = "NextPressed"; + protected final static List KEY_BINDINGS = new ArrayList(); + static + { + KEY_BINDINGS.add( new KeyBinding(KeyCode.MINUS, EVENT_PREVIOUS) ); // keyboard - + KEY_BINDINGS.add( new KeyBinding(KeyCode.PLUS, EVENT_NEXT) ); // keyboard + + KEY_BINDINGS.add( new KeyBinding(KeyCode.SUBTRACT, EVENT_PREVIOUS) ); // keypad - + KEY_BINDINGS.add( new KeyBinding(KeyCode.ADD, EVENT_NEXT) ); // keypad + + KEY_BINDINGS.add(new KeyBinding(KeyCode.UP, EVENT_NEXT)); + KEY_BINDINGS.add(new KeyBinding(KeyCode.DOWN, EVENT_PREVIOUS)); + KEY_BINDINGS.add(new KeyBinding(KeyCode.LEFT, EVENT_PREVIOUS)); + KEY_BINDINGS.add(new KeyBinding(KeyCode.RIGHT, EVENT_NEXT)); + KEY_BINDINGS.addAll(TRAVERSAL_BINDINGS); + } + + @Override protected List createKeyBindings() + { + return KEY_BINDINGS; + } + + @Override protected void callAction(String name) { + if (EVENT_PREVIOUS.equals(name)) { + getControl().decrement(); + } + else if (EVENT_NEXT.equals(name)) { + getControl().increment(); + } + else { + super.callAction(name); + } + } +} diff --git a/DuskZ/src/jfxtras/labs/internal/scene/control/skin/ListSpinnerCaspianSkin.java b/DuskZ/src/jfxtras/labs/internal/scene/control/skin/ListSpinnerCaspianSkin.java new file mode 100644 index 0000000..4cb3b4e --- /dev/null +++ b/DuskZ/src/jfxtras/labs/internal/scene/control/skin/ListSpinnerCaspianSkin.java @@ -0,0 +1,560 @@ +/** + * Copyright (c) 2011, JFXtras + * 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 the 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 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. + */ +package jfxtras.labs.internal.scene.control.skin; + +import javafx.beans.InvalidationListener; +import javafx.beans.Observable; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.geometry.Point2D; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseEvent; +import javafx.scene.input.ScrollEvent; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.ColumnConstraints; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; +import javafx.scene.layout.RowConstraints; +import javafx.util.Duration; +import jfxtras.labs.animation.Timer; +import jfxtras.labs.internal.scene.control.behavior.ListSpinnerBehavior; +import jfxtras.labs.scene.control.ListSpinner; +import jfxtras.labs.scene.control.ListSpinner.ArrowDirection; +import jfxtras.labs.scene.control.ListSpinner.ArrowPosition; + +import com.sun.javafx.scene.control.skin.SkinBase; + +/** + * + * @author Tom Eugelink + * + * Possible extension: drop down list or grid for quick selection + */ +public class ListSpinnerCaspianSkin extends SkinBase, ListSpinnerBehavior> +{ + // TODO: vertical centering + + // ================================================================================================================== + // CONSTRUCTOR + + /** + * + */ + public ListSpinnerCaspianSkin(ListSpinner control) + { + super(control, new ListSpinnerBehavior(control)); + construct(); + } + + /* + * + */ + private void construct() + { + // setup component + createNodes(); + + // react to value changes in the model + getSkinnable().editableProperty().addListener(new ChangeListener() + { + @Override + public void changed(ObservableValue arg0, Boolean arg1, Boolean arg2) + { + replaceValueNode(); + } + }); + replaceValueNode(); + + // react to value changes in the model + getSkinnable().valueProperty().addListener(new ChangeListener() + { + @Override + public void changed(ObservableValue observableValue, T oldValue, T newValue) + { + refreshValue(); + } + }); + refreshValue(); + + // react to value changes in the model + getSkinnable().arrowDirectionProperty().addListener(new ChangeListener() + { + @Override + public void changed(ObservableValue observableValue, ArrowDirection oldValue, ArrowDirection newValue) + { + setArrowCSS(); + layoutGridPane(); + } + }); + setArrowCSS(); + layoutGridPane(); + + // react to value changes in the model + getSkinnable().alignmentProperty().addListener(new ChangeListener() + { + @Override + public void changed(ObservableValue observableValue, Pos oldValue, Pos newValue) + { + alignValue(); + } + }); + alignValue(); + + } + + /* + * + */ + private void refreshValue() + { + // if editable + if (getSkinnable().isEditable() == true) + { + // update textfield + T lValue = getSkinnable().getValue(); + textField.setText( getSkinnable().getPrefix() + getSkinnable().getStringConverter().toString(lValue) + getSkinnable().getPostfix() ); + } + else + { + // get node for this value + Node lNode = getSkinnable().getCellFactory().call( getSkinnable() ); + } + } + + // ================================================================================================================== + // DRAW + + /** + * Construct the nodes. + * Spinner uses a GridPane where the arrows and the node for the value are laid out according to the arrows direction and location. + * A place holder in inserted into the GridPane to hold the value node, so the spinner can alternate between editable or readonly mode, without having to recreate the GridPane. + */ + private void createNodes() + { + // left arrow + decrementArrow = new Region(); + decrementArrow.getStyleClass().add("idle"); + + // place holder for showing the value + valueGroup = new BorderPane(); + valueGroup.getStyleClass().add("valuePane"); + + // right arrow + incrementArrow = new Region(); + incrementArrow.getStyleClass().add("idle"); + + // construct a gridpane + gridPane = new GridPane(); + + // we're not catching the mouse events on the individual children, but let it bubble up to the parent and handle it there, this makes our life much more simple + // process mouse clicks + gridPane.setOnMouseClicked(new EventHandler() + { + @Override public void handle(MouseEvent evt) + { + // if click was the in the greater vicinity of the decrement arrow + if (mouseEventOverArrow(evt, decrementArrow)) + { + // left + unclickArrows(); + decrementArrow.getStyleClass().add("clicked"); + getSkinnable().decrement(); + unclickTimer.restart(); + return; + } + + // if click was the in the greater vicinity of the increment arrow + if (mouseEventOverArrow(evt, incrementArrow)) + { + // right + unclickArrows(); + incrementArrow.getStyleClass().add("clicked"); + getSkinnable().increment(); + unclickTimer.restart(); + return; + } + } + }); + // process mouse holds + gridPane.setOnMousePressed(new EventHandler() + { + @Override public void handle(MouseEvent evt) + { + // if click was the in the greater vicinity of the decrement arrow + if (mouseEventOverArrow(evt, decrementArrow)) + { + // left + decrementArrow.getStyleClass().add("clicked"); + repeatDecrementClickTimer.restart(); + return; + } + + // if click was the in the greater vicinity of the increment arrow + if (mouseEventOverArrow(evt, incrementArrow)) + { + // right + incrementArrow.getStyleClass().add("clicked"); + repeatIncrementClickTimer.restart(); + return; + } + } + }); + gridPane.setOnMouseReleased(new EventHandler() + { + @Override public void handle(MouseEvent evt) + { + unclickArrows(); + repeatDecrementClickTimer.stop(); + repeatIncrementClickTimer.stop(); + } + }); + gridPane.setOnMouseExited(new EventHandler() + { + @Override public void handle(MouseEvent evt) + { + unclickArrows(); + repeatDecrementClickTimer.stop(); + repeatIncrementClickTimer.stop(); + } + }); + // mouse wheel + gridPane.setOnScroll(new EventHandler() + { + @Override + public void handle(ScrollEvent evt) + { + // if click was the in the greater vicinity of the decrement arrow + if (evt.getDeltaY() < 0 || evt.getDeltaX() < 0) + { + // left + unclickArrows(); + decrementArrow.getStyleClass().add("clicked"); + getSkinnable().decrement(); + unclickTimer.restart(); + return; + } + + // if click was the in the greater vicinity of the increment arrow + if (evt.getDeltaY() > 0 || evt.getDeltaX() > 0) + { + // right + unclickArrows(); + incrementArrow.getStyleClass().add("clicked"); + getSkinnable().increment(); + unclickTimer.restart(); + return; + } + } + }); + + // add to self + this.getStyleClass().add(this.getClass().getSimpleName()); // always add self as style class, because CSS should relate to the skin not the control + getChildren().add(gridPane); + } + private Region decrementArrow = null; + private Region incrementArrow = null; + private GridPane gridPane = null; + private BorderPane valueGroup; + + // timer to remove the click styling on the arrows after a certain delay + final private Timer unclickTimer = new Timer(new Runnable() + { + @Override + public void run() + { + unclickArrows(); + } + }).withDelay(Duration.millis(100)).withRepeats(false); + + // timer to handle the holding of the decrement button + final private Timer repeatDecrementClickTimer = new Timer(new Runnable() + { + @Override + public void run() + { + getSkinnable().decrement(); + } + }).withDelay(Duration.millis(500)).withCycleDuration(Duration.millis(50)); + + // timer to handle the holding of the increment button + final private Timer repeatIncrementClickTimer = new Timer(new Runnable() + { + @Override + public void run() + { + getSkinnable().increment(); + } + }).withDelay(Duration.millis(500)).withCycleDuration(Duration.millis(50)); + + /** + * Check if the mouse event is considered to have happened over the arrow + * @param evt + * @param region + * @return + */ + private boolean mouseEventOverArrow(MouseEvent evt, Region region) + { + // if click was the in the greater vicinity of the decrement arrow + Point2D lClickInRelationToArrow = region.sceneToLocal(evt.getSceneX(), evt.getSceneY()); + if ( lClickInRelationToArrow.getX() >= 0.0 && lClickInRelationToArrow.getX() <= region.getWidth() + && lClickInRelationToArrow.getY() >= 0.0 && lClickInRelationToArrow.getY() <= region.getHeight() + ) + { + return true; + } + return false; + } + + /** + * Remove clicked CSS styling from the arrows + */ + private void unclickArrows() + { + decrementArrow.getStyleClass().remove("clicked"); + incrementArrow.getStyleClass().remove("clicked"); + } + + /** + * Put the correct node for the value's place holder: + * - either the TextField when in editable mode, + * - or a node generated by the cell factory when in readonly mode. + */ + private void replaceValueNode() + { + // clear + valueGroup.getChildren().clear(); + + // if not editable + if (getSkinnable().isEditable() == false) + { + // use the cell factory + Node lNode = getSkinnable().getCellFactory().call(getSkinnable()); + valueGroup.setCenter( lNode ); + if (lNode.getStyleClass().contains("value") == false) lNode.getStyleClass().add("value"); + if (lNode.getStyleClass().contains("readonly") == false) lNode.getStyleClass().add("readonly"); + } + else + { + // use the textfield + if (textField == null) + { + textField = new TextField(); + textField.getStyleClass().add("value"); + textField.getStyleClass().add("editable"); + + // process text entry + textField.focusedProperty().addListener(new InvalidationListener() + { + @Override + public void invalidated(Observable arg0) + { + if (textField.isFocused() == false) + { + parse(textField); + } + } + }); + textField.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent evt) + { + parse(textField); + } + }); + textField.setOnKeyPressed(new EventHandler() + { + @Override public void handle(KeyEvent t) + { + if (t.getCode() == KeyCode.ESCAPE) + { + // refresh + refreshValue(); + } + } + }); + + // alignment + textField.alignmentProperty().bind(getSkinnable().alignmentProperty()); + } + valueGroup.setCenter(textField); + } + + // align + alignValue(); + } + private TextField textField = null; + + /** + * align the value inside the plave holder + */ + private void alignValue() + { + // valueGroup always only holds one child (the value) + BorderPane.setAlignment(valueGroup.getChildren().get(0), getSkinnable().alignmentProperty().getValue()); + } + + // ================================================================================================================== + // EDITABLE + + /** + * Parse the contents of the textfield + * @param textField + */ + protected void parse(TextField textField) + { + // get the text to parse + String lText = textField.getText(); + + // process it + getBehavior().parse(lText); + + // refresh + refreshValue(); + return; + } + + /** + * Lays out the spinner, depending on the location and direction of the arrows. + */ + private void layoutGridPane() + { + // get the things we decide on + ArrowDirection lArrowDirection = getSkinnable().getArrowDirection(); + ArrowPosition lArrowPosition = getSkinnable().getArrowPosition(); + + // get helper values + ColumnConstraints lColumnValue = new ColumnConstraints(valueGroup.getMinWidth(), valueGroup.getPrefWidth(), Double.MAX_VALUE); + lColumnValue.setHgrow(Priority.ALWAYS); + ColumnConstraints lColumnArrow = new ColumnConstraints(10); + + // get helper values + RowConstraints lRowValue = new RowConstraints(valueGroup.getMinHeight(), valueGroup.getPrefHeight(), Double.MAX_VALUE); + lRowValue.setVgrow(Priority.ALWAYS); + RowConstraints lRowArrow = new RowConstraints(10); + + // clear the grid + gridPane.getChildren().clear(); + gridPane.getColumnConstraints().clear(); + gridPane.getRowConstraints().clear(); + //gridPane.setGridLinesVisible(true); + + if (lArrowDirection == ArrowDirection.HORIZONTAL) + { + if (lArrowPosition == ArrowPosition.LEADING) + { + // construct a gridpane: one row, three columns: arrow, arrow, value + gridPane.setHgap(3); + gridPane.setVgap(0); + gridPane.add(decrementArrow, 0, 0); + gridPane.add(incrementArrow, 1, 0); + gridPane.add(valueGroup, 2, 0); + gridPane.getColumnConstraints().addAll(lColumnArrow, lColumnArrow, lColumnValue); + } + if (lArrowPosition == ArrowPosition.TRAILING) + { + // construct a gridpane: one row, three columns: value, arrow, arrow + gridPane.setHgap(3); + gridPane.setVgap(0); + gridPane.add(valueGroup, 0, 0); + gridPane.add(decrementArrow, 1, 0); + gridPane.add(incrementArrow, 2, 0); + gridPane.getColumnConstraints().addAll(lColumnValue, lColumnArrow, lColumnArrow); + } + if (lArrowPosition == ArrowPosition.SPLIT) + { + // construct a gridpane: one row, three columns: arrow, value, arrow + gridPane.setHgap(3); + gridPane.setVgap(0); + gridPane.add(decrementArrow, 0, 0); + gridPane.add(valueGroup, 1, 0); + gridPane.add(incrementArrow, 2, 0); + gridPane.getColumnConstraints().addAll(lColumnArrow, lColumnValue, lColumnArrow); + } + } + if (lArrowDirection == ArrowDirection.VERTICAL) + { + if (lArrowPosition == ArrowPosition.LEADING) + { + // construct a gridpane: two rows, two columns: arrows on top, value + gridPane.setHgap(3); + gridPane.setVgap(0); + gridPane.add(incrementArrow, 0, 0); + gridPane.add(decrementArrow, 0, 1); + gridPane.add(valueGroup, 1, 0, 1, 2); + gridPane.getColumnConstraints().addAll(lColumnArrow, lColumnValue); + gridPane.getRowConstraints().addAll(lRowArrow, lRowArrow); + } + if (lArrowPosition == ArrowPosition.TRAILING) + { + // construct a gridpane: two rows, two columns: value, arrows on top + gridPane.setHgap(3); + gridPane.setVgap(0); + gridPane.add(valueGroup, 0, 0, 1, 2); + gridPane.add(incrementArrow, 1, 0); + gridPane.add(decrementArrow, 1, 1); + gridPane.getColumnConstraints().addAll(lColumnValue, lColumnArrow); + gridPane.getRowConstraints().addAll(lRowArrow, lRowArrow); + } + if (lArrowPosition == ArrowPosition.SPLIT) + { + // construct a gridpane: three rows, one columns: arrow, value, arrow + gridPane.setHgap(3); + gridPane.setVgap(0); + gridPane.add(incrementArrow, 0, 0); + gridPane.add(valueGroup, 0, 1); + gridPane.add(decrementArrow, 0, 2); + gridPane.getColumnConstraints().addAll(lColumnValue); + gridPane.getRowConstraints().addAll(lRowArrow, lRowValue, lRowArrow); + } + } + } + + /** + * Set the CSS according to the direction of the arrows, so the correct arrows are shown + */ + private void setArrowCSS() + { + if (getSkinnable().getArrowDirection().equals(ListSpinner.ArrowDirection.HORIZONTAL)) + { + decrementArrow.getStyleClass().add("left-arrow"); + incrementArrow.getStyleClass().add("right-arrow"); + } + else + { + decrementArrow.getStyleClass().add("down-arrow"); + incrementArrow.getStyleClass().add("up-arrow"); + } + } +} diff --git a/DuskZ/src/jfxtras/labs/scene/control/ListSpinner.java b/DuskZ/src/jfxtras/labs/scene/control/ListSpinner.java new file mode 100644 index 0000000..457c238 --- /dev/null +++ b/DuskZ/src/jfxtras/labs/scene/control/ListSpinner.java @@ -0,0 +1,613 @@ +/** + * Copyright (c) 2011, JFXtras + * 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 the 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 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. + */ +package jfxtras.labs.scene.control; + +import java.util.Arrays; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.event.Event; +import javafx.event.EventHandler; +import javafx.event.EventTarget; +import javafx.event.EventType; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Control; +import javafx.scene.control.Label; +import javafx.util.Callback; +import javafx.util.StringConverter; + +/** + * This is a spinner, showing one value at a time from a list. + * This value is set and retrieved from the value property. + * Basically a spinner shows a list of values and can do "next" or "previous" on this. + * + * A spinner can be editable, the user can then type a value instead of selecting it. + * If the value exists in the list, the spinner will simply jump to it. + * If the value does not exist, if defined the AddCallback is called. + * - If the AddCallback returns null, spinner will only refresh the current index. + * - If the AddCallback returns an Integer, spinner will jump to that index (usually the index where the new value was added). + * + * http://openjdk.java.net/projects/openjfx/ux/spinner/index.html + * + * You can style the text in the control using CSS like so: + * .ListSpinner .value { + * -fx-font-weight: bold; + * } + * + * The "value" class applies to the text in both readonly and editable spinners. Use "readonly" or "editable" to style either mode specifically. + * + * @author Tom Eugelink + */ +public class ListSpinner extends Control +{ + // TODO: implement SelectionModel? + // ================================================================================================================== + // CONSTRUCTOR + + /** + */ + public ListSpinner() + { + construct(); + } + + // ------------ + // model + + /** + * @param items The item list used to populate the spinner. + */ + public ListSpinner(ObservableList items) + { + construct(); + setItems(items); + first(); + } + + /** + * @param items The item list used to populate the spinner. + * @param startValue The initial value of the spinner (one of the items). + */ + public ListSpinner(ObservableList items, T startValue) + { + construct(); + setItems(items); + setValue(startValue); + } + + // ------------ + // convenience + + /** + * @param list + */ + public ListSpinner(java.util.List list) + { + this( FXCollections.observableList(list) ); + } + + /** + * @param list + */ + public ListSpinner(T... list) + { + this( Arrays.asList(list) ); + } + + /** + * @param from + * @param to + */ + public ListSpinner(int from, int to) + { + this( (java.util.List) new ListSpinnerIntegerList(from, to) ); + } + + /** + * @param from + * @param to + * @param step + */ + public ListSpinner(int from, int to, int step) + { + this( (java.util.List) new ListSpinnerIntegerList(from, to, step) ); + } + + // ------------ + + /* + * + */ + private void construct() + { + // setup the CSS + // the -fx-skin attribute in the CSS sets which Skin class is used + this.getStyleClass().add(this.getClass().getSimpleName()); + + // react to changes of the value + this.valueObjectProperty.addListener(new ChangeListener() + { + @Override + public void changed(ObservableValue property, T oldValue, T newValue) + { + // get the value of the new index + int lIdx = getItems().indexOf(newValue); + + // set the value + if (ListSpinner.equals(indexObjectProperty.getValue(), lIdx) == false) + { + indexObjectProperty.setValue(lIdx); + } + } + }); + + // react to changes of the index + this.indexObjectProperty.addListener(new ChangeListener() + { + @Override + public void changed(ObservableValue property, Integer oldIndex, Integer newIndex) + { + // get the value of the new index + T lValue = newIndex < 0 ? null : getItems().get(newIndex); + + // set the value + if (ListSpinner.equals(valueObjectProperty.getValue(), lValue) == false) + { + valueObjectProperty.setValue(lValue); + } + } + }); + + // react to changes of the items + this.itemsObjectProperty.addListener(new ChangeListener>() + { + @Override + public void changed(ObservableValue> property, ObservableList oldList, ObservableList newList) + { + if (oldList != null) oldList.removeListener(listChangeListener); + if (newList != null) newList.addListener(listChangeListener); + } + }); + } + + /* + * react to observable list changes + * TODO: what is sticky, index or value? Now: index + */ + private ListChangeListener listChangeListener = new ListChangeListener() + { + @Override + public void onChanged(javafx.collections.ListChangeListener.Change change) + { + // get current index + int lIndex = getIndex(); + + // is it still valid? + if (lIndex >= getItems().size()) + { + lIndex = getItems().size() - 1; + setIndex(lIndex); + return; + } + + // (re)set the value of the index + valueObjectProperty.setValue( getItems().get(lIndex) ); + } + }; + + /** + * Return the path to the CSS file so things are setup right + */ + @Override protected String getUserAgentStylesheet() + { + return this.getClass().getResource("/jfxtras/labs/internal/scene/control/" + this.getClass().getSimpleName() + ".css").toString(); + } + + // ================================================================================================================== + // PROPERTIES + + /** Value: */ + public ObjectProperty valueProperty() { return this.valueObjectProperty; } + final private ObjectProperty valueObjectProperty = new SimpleObjectProperty(this, "value", null) + { + public void set(T value) + { + if (getItems().indexOf(value) < 0) throw new IllegalArgumentException("Value does not exist in the list: " + value); + super.set(value); + } + }; + // java bean API + public T getValue() { return this.valueObjectProperty.getValue(); } + public void setValue(T value) { this.valueObjectProperty.setValue(value); } + public ListSpinner withValue(T value) { setValue(value); return this; } + + /** Index: */ + public ObjectProperty indexProperty() { return this.indexObjectProperty; } + final private ObjectProperty indexObjectProperty = new SimpleObjectProperty(this, "index", null) + { + public void set(Integer value) + { + if (value == null) throw new NullPointerException("Null not allowed as the value for index"); + if (value >= getItems().size()) throw new IllegalArgumentException("Index out of bounds: " + value + ", valid values are 0-" + (getItems().size() - 1)); + super.set(value); + } + }; + public Integer getIndex() { return this.indexObjectProperty.getValue(); } + public void setIndex(Integer value) { this.indexObjectProperty.setValue(value); } + public ListSpinner withIndex(Integer value) { setIndex(value); return this; } + + /** Cyclic: */ + public ObjectProperty cyclicProperty() { return this.cyclicObjectProperty; } + final private ObjectProperty cyclicObjectProperty = new SimpleObjectProperty(this, "cyclic", false) + { + public void set(Boolean value) + { + if (value == null) throw new NullPointerException("Null not allowed as the value for cyclic"); + super.set(value); + } + }; + public Boolean isCyclic() { return this.cyclicObjectProperty.getValue(); } + public void setCyclic(Boolean value) { this.cyclicObjectProperty.setValue(value); } + public ListSpinner withCyclic(Boolean value) { setCyclic(value); return this; } + + /** Editable: */ + public ObjectProperty editableProperty() { return this.editableObjectProperty; } + final private ObjectProperty editableObjectProperty = new SimpleObjectProperty(this, "editable", false) + { + public void set(Boolean value) + { + if (value == null) throw new NullPointerException("Null not allowed as the value for editable"); + super.set(value); + } + }; + public Boolean isEditable() { return this.editableObjectProperty.getValue(); } + public void setEditable(Boolean value) { this.editableObjectProperty.setValue(value); } + public ListSpinner withEditable(Boolean value) { setEditable(value); return this; } + + /** Postfix: */ + public ObjectProperty postfixProperty() { return this.postfixObjectProperty; } + final private ObjectProperty postfixObjectProperty = new SimpleObjectProperty(this, "postfix", ""); + public String getPostfix() { return this.postfixObjectProperty.getValue(); } + public void setPostfix(String value) { this.postfixObjectProperty.setValue(value); } + public ListSpinner withPostfix(String value) { setPostfix(value); return this; } + + /** Prefix: */ + public ObjectProperty prefixProperty() { return this.prefixObjectProperty; } + final private ObjectProperty prefixObjectProperty = new SimpleObjectProperty(this, "prefix", ""); + public String getPrefix() { return this.prefixObjectProperty.getValue(); } + public void setPrefix(String value) { this.prefixObjectProperty.setValue(value); } + public ListSpinner withPrefix(String value) { setPrefix(value); return this; } + + /** Items: */ + public ObjectProperty> itemsProperty() { return this.itemsObjectProperty; } + final private ObjectProperty> itemsObjectProperty = new SimpleObjectProperty>(this, "items", null) + { + public void set(ObservableList value) + { + if (value == null) throw new NullPointerException("Null not allowed as the value for items"); + super.set(value); + } + }; + public ObservableList getItems() { return this.itemsObjectProperty.getValue(); } + public void setItems(ObservableList value) { this.itemsObjectProperty.setValue(value); } + public ListSpinner withItems(ObservableList value) { setItems(value); return this; } + + /** CellFactory: */ + public ObjectProperty, Node>> cellFactoryProperty() { return this.cellFactoryObjectProperty; } + final private ObjectProperty, Node>> cellFactoryObjectProperty = new SimpleObjectProperty, Node>>(this, "cellFactory", new DefaultCellFactory()); + public Callback, Node> getCellFactory() { return this.cellFactoryObjectProperty.getValue(); } + public void setCellFactory(Callback, Node> value) { this.cellFactoryObjectProperty.setValue(value); } + public ListSpinner withCellFactory(Callback, Node> value) { setCellFactory(value); return this; } + + /** StringConverter: */ + public ObjectProperty> stringConverterProperty() { return this.stringConverterObjectProperty; } + final private ObjectProperty> stringConverterObjectProperty = new SimpleObjectProperty>(this, "stringConverter", new DefaultStringConverter()); + public StringConverter getStringConverter() { return this.stringConverterObjectProperty.getValue(); } + public void setStringConverter(StringConverter value) { this.stringConverterObjectProperty.setValue(value); } + public ListSpinner withStringConverter(StringConverter value) { setStringConverter(value); return this; } + + /** ArrowDirection: */ + public ObjectProperty arrowDirectionProperty() { return this.arrowDirectionObjectProperty; } + final private ObjectProperty arrowDirectionObjectProperty = new SimpleObjectProperty(this, "arrowDirection", ArrowDirection.HORIZONTAL) + { + public void set(ArrowDirection value) + { + if (value == null) throw new NullPointerException("Null not allowed as the value for arrowDirection"); + super.set(value); + } + }; + public ArrowDirection getArrowDirection() { return this.arrowDirectionObjectProperty.getValue(); } + public void setArrowDirection(ArrowDirection value) { this.arrowDirectionObjectProperty.setValue(value); } + public ListSpinner withArrowDirection(ArrowDirection value) { setArrowDirection(value); return this; } + public enum ArrowDirection {VERTICAL, HORIZONTAL} + + /** ArrowPosition: */ + public ObjectProperty arrowPositionProperty() { return this.arrowPositionObjectProperty; } + final private ObjectProperty arrowPositionObjectProperty = new SimpleObjectProperty(this, "arrowPosition", ArrowPosition.TRAILING) + { + public void set(ArrowPosition value) + { + if (value == null) throw new NullPointerException("Null not allowed as the value for arrowPosition"); + super.set(value); + } + }; + public ArrowPosition getArrowPosition() { return this.arrowPositionObjectProperty.getValue(); } + public void setArrowPosition(ArrowPosition value) { this.arrowPositionObjectProperty.setValue(value); } + public ListSpinner withArrowPosition(ArrowPosition value) { setArrowPosition(value); return this; } + public enum ArrowPosition {LEADING, TRAILING, SPLIT} + + /** Alignment: only applicable in non edit mode */ + public ObjectProperty alignmentProperty() { return this.alignmentObjectProperty; } + final private ObjectProperty alignmentObjectProperty = new SimpleObjectProperty(this, "alignment", Pos.CENTER_LEFT) + { + public void set(Pos value) + { + if (value == null) throw new NullPointerException("Null not allowed as the value for alignment"); + super.set(value); + } + }; + public Pos isAlignment() { return this.alignmentObjectProperty.getValue(); } + public void setAlignment(Pos value) { this.alignmentObjectProperty.setValue(value); } + public ListSpinner withAlignment(Pos value) { setAlignment(value); return this; } + + /** AddCallback: */ + public ObjectProperty> addCallbackProperty() { return this.addCallbackObjectProperty; } + final private ObjectProperty> addCallbackObjectProperty = new SimpleObjectProperty>(this, "addCallback", null); + public Callback getAddCallback() { return this.addCallbackObjectProperty.getValue(); } + public void setAddCallback(Callback value) { this.addCallbackObjectProperty.setValue(value); } + public ListSpinner withAddCallback(Callback value) { setAddCallback(value); return this; } + + // ================================================================================================================== + // StringConverter + + /** + * A string converter that does a simple toString, but cannot convert to an object + * @see org.jfxextras.util.StringConverterFactory + */ + class DefaultStringConverter extends StringConverter + { + @Override + public T fromString(String string) + { + throw new IllegalStateException("No StringConverter is set. An editable Spinner must have a StringConverter to be able to render and parse the value."); + } + + @Override + public String toString(T value) + { + return value == null ? "" : value.toString(); + } + } + + // ================================================================================================================== + // CellFactory + + /** + * Default cell factory + */ + class DefaultCellFactory implements Callback, Node> + { + private Label label = null; + + @Override + public Node call(ListSpinner spinner) + { + // get value + T lValue = spinner.getValue(); + + // label not yet created + if (this.label == null) + { + this.label = new Label(); + } + this.label.setText( lValue == null ? "" : spinner.getPrefix() + getStringConverter().toString(lValue) + spinner.getPostfix() ); + return this.label; + } + }; + + // ================================================================================================================== + // EVENTS + + /** OnCycle: */ + public ObjectProperty> onCycleProperty() { return iOnCycleObjectProperty; } + final private ObjectProperty> iOnCycleObjectProperty = new SimpleObjectProperty>(null); + // java bean API + public EventHandler getOnCycle() { return iOnCycleObjectProperty.getValue(); } + public void setOnCycle(EventHandler value) { iOnCycleObjectProperty.setValue(value); } + public ListSpinner withOnCycle(EventHandler value) { setOnCycle(value); return this; } + final static public String ONCYCLE_PROPERTY_ID = "onCycle"; + + /** + * CycleEvent + */ + static public class CycleEvent extends Event + { + /** + * + */ + public CycleEvent() + { + super(new EventType()); + } + + /** + * + * @param source + * @param target + */ + public CycleEvent(Object source, EventTarget target) + { + super(source, target, new EventType()); + } + + public Object getOldIdx() { return this.oldIdx; } + private Object oldIdx; + + public Object getNewIdx() { return this.newIdx; } + private Object newIdx; + + + public boolean cycledDown() { return cycleDirection == CycleDirection.TOP_TO_BOTTOM; } + public boolean cycledUp() { return cycleDirection == CycleDirection.BOTTOM_TO_TOP; } + CycleDirection cycleDirection; + } + + /** + * we're cycling, fire the event + */ + public void fireCycleEvent(CycleDirection cycleDirection) + { + EventHandler lCycleEventHandler = getOnCycle(); + if (lCycleEventHandler != null) + { + CycleEvent lCycleEvent = new CycleEvent(); + lCycleEvent.cycleDirection = cycleDirection; + lCycleEventHandler.handle(lCycleEvent); + } + } + static public enum CycleDirection { TOP_TO_BOTTOM, BOTTOM_TO_TOP } + + + // ================================================================================================================== + // BEHAVIOR + + /** + * + */ + public void first() + { + // nothing to do + if (getItems() == null || getItems().size() == 0) return; + + // set the new index (this will update the value) + indexObjectProperty.setValue(0); + } + + /** + * + */ + public void decrement() + { + // nothing to do + if (getItems() == null || getItems().size() == 0) return; + + // get the current index + int lOldIdx = this.indexObjectProperty.getValue(); + + // get the previous index (usually current - 1) + int lIdx = lOldIdx - 1; + + // if end + if (lIdx < 0) + { + // if we're not cyclic + if (isCyclic() != null && isCyclic().booleanValue() == false) + { + // do nothing + return; + } + + // cycle to the other end: get the last value + lIdx = getItems().size() - 1; + + // notify listener that we've cycled + fireCycleEvent(CycleDirection.BOTTOM_TO_TOP); + } + + // set the new index (this will update the value) + indexObjectProperty.setValue(lIdx); + } + + /** + * + */ + public void increment() + { + // nothing to do + if (getItems() == null || getItems().size() == 0) return; + + // get the current index + int lOldIdx = this.indexObjectProperty.getValue(); + + // get the next index (usually current + 1) + int lIdx = lOldIdx + 1; + + // if null is return, there is no next index (usually current + 1) + if (lIdx >= getItems().size()) + { + // if we're not cyclic + if (isCyclic() != null && isCyclic().booleanValue() == false) + { + // do nothing + return; + } + + // cycle to the other end: get the first value + lIdx = 0; + + // notify listener that we've cycled + fireCycleEvent(CycleDirection.TOP_TO_BOTTOM); + } + + // set the new index (this will update the value) + indexObjectProperty.setValue(lIdx); + } + + /** + * Get the last index; if the data provide is endless, this method mail fail! + */ + public void last() + { + // nothing to do + if (getItems() == null || getItems().size() == 0) return; + + // set the new index (this will update the value) + indexObjectProperty.setValue(getItems().size() - 1); + } + + /** + * Does a o1.equals(o2) but also checks if o1 or o2 are null. + * @param o1 + * @param o2 + * @return True if the two values are equal, false otherwise. + */ + static public boolean equals(Object o1, Object o2) + { + if ( o1 == null && o2 == null ) return true; + if ( o1 != null && o2 == null ) return false; + if ( o1 == null && o2 != null ) return false; + // TODO: compare arrays if (o1.getClass().isArray() && o2.getClass().isArray()) return Arrays.equals( (Object[])o1, (Object[])o2 ); + return o1.equals(o2); + } + +} diff --git a/DuskZ/src/jfxtras/labs/scene/control/ListSpinnerIntegerList.java b/DuskZ/src/jfxtras/labs/scene/control/ListSpinnerIntegerList.java new file mode 100644 index 0000000..e9c50f5 --- /dev/null +++ b/DuskZ/src/jfxtras/labs/scene/control/ListSpinnerIntegerList.java @@ -0,0 +1,76 @@ +package jfxtras.labs.scene.control; + +/** + * Items for Spinner providing an integer range without actually creating a list with all values. + */ +public class ListSpinnerIntegerList extends java.util.AbstractList +{ + /** + * + */ + public ListSpinnerIntegerList() + { + this( (Integer.MIN_VALUE / 2) + 1, Integer.MAX_VALUE / 2, 1); + } + + /** + * + * @param from + * @param to + */ + public ListSpinnerIntegerList(int from, int to) + { + this(from, to, from > to ? -1 : 1); + } + + /** + * + * @param from + * @param to + * @param step + */ + public ListSpinnerIntegerList(int from, int to, int step) + { + this.from = from; + this.size = ((to - from) / step) + 1; + if (size < 0) throw new IllegalArgumentException("This results in a negative size: " + from + ", " + to + "," + step); + this.step = step; + } + private int from; + private int size; + private int step; + + + // =============================================================================== + // List interface + + @Override + public Integer get(int index) + { + if (index < 0) throw new IllegalArgumentException("Index cannot be < 0: " + index); + int lValue = this.from + (index * this.step); + return lValue; + } + + @Override + public int indexOf(Object o) + { + // calculate the index + int lValue = ((Integer)o).intValue(); + int lIndex = (lValue - this.from) / this.step; + if (lIndex < 0 || lIndex > size) return -1; + + // check if that what is at the index matches with out value + Integer lValueAtIndex = get(lIndex); + if (o.equals(lValueAtIndex) == false) return -1; + + // found it + return lIndex; + } + + @Override + public int size() + { + return this.size; + } +} \ No newline at end of file